/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"WorkerPrivate.h"#include"amIAddonManager.h"#include"nsIClassInfo.h"#include"nsIContentSecurityPolicy.h"#include"nsIConsoleService.h"#include"nsIDOMDOMException.h"#include"nsIDOMEvent.h"#include"nsIDocument.h"#include"nsIDocShell.h"#include"nsIInterfaceRequestor.h"#include"nsIMemoryReporter.h"#include"nsINetworkInterceptController.h"#include"nsIPermissionManager.h"#include"nsIScriptError.h"#include"nsIScriptGlobalObject.h"#include"nsIScriptSecurityManager.h"#include"nsIScriptTimeoutHandler.h"#include"nsITabChild.h"#include"nsITextToSubURI.h"#include"nsIThreadInternal.h"#include"nsITimer.h"#include"nsIURI.h"#include"nsIURL.h"#include"nsIWeakReferenceUtils.h"#include"nsIWorkerDebugger.h"#include"nsIXPConnect.h"#include"nsPIDOMWindow.h"#include"nsGlobalWindow.h"#include<algorithm>#include"ImageContainer.h"#include"jsfriendapi.h"#include"js/MemoryMetrics.h"#include"mozilla/Assertions.h"#include"mozilla/Attributes.h"#include"mozilla/ContentEvents.h"#include"mozilla/EventDispatcher.h"#include"mozilla/Likely.h"#include"mozilla/LoadContext.h"#include"mozilla/Move.h"#include"mozilla/dom/BindingUtils.h"#include"mozilla/dom/Console.h"#include"mozilla/dom/DocGroup.h"#include"mozilla/dom/ErrorEvent.h"#include"mozilla/dom/ErrorEventBinding.h"#include"mozilla/dom/Exceptions.h"#include"mozilla/dom/ExtendableMessageEventBinding.h"#include"mozilla/dom/FunctionBinding.h"#include"mozilla/dom/IndexedDatabaseManager.h"#include"mozilla/dom/MessageEvent.h"#include"mozilla/dom/MessageEventBinding.h"#include"mozilla/dom/MessagePort.h"#include"mozilla/dom/MessagePortBinding.h"#include"mozilla/dom/nsCSPUtils.h"#include"mozilla/dom/Performance.h"#include"mozilla/dom/PMessagePort.h"#include"mozilla/dom/Promise.h"#include"mozilla/dom/PromiseDebugging.h"#include"mozilla/dom/PromiseNativeHandler.h"#include"mozilla/dom/SimpleGlobalObject.h"#include"mozilla/dom/ScriptSettings.h"#include"mozilla/dom/StructuredCloneHolder.h"#include"mozilla/dom/TabChild.h"#include"mozilla/dom/WorkerBinding.h"#include"mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"#include"mozilla/dom/WorkerGlobalScopeBinding.h"#include"mozilla/Preferences.h"#include"mozilla/SizePrintfMacros.h"#include"mozilla/ThrottledEventQueue.h"#include"mozilla/TimelineConsumers.h"#include"mozilla/WorkerTimelineMarker.h"#include"nsAlgorithm.h"#include"nsContentUtils.h"#include"nsCycleCollector.h"#include"nsError.h"#include"nsDOMJSUtils.h"#include"nsHostObjectProtocolHandler.h"#include"nsJSEnvironment.h"#include"nsJSUtils.h"#include"nsNetUtil.h"#include"nsPrintfCString.h"#include"nsProxyRelease.h"#include"nsQueryObject.h"#include"nsSandboxFlags.h"#include"nsScriptError.h"#include"nsUTF8Utils.h"#include"prthread.h"#include"xpcpublic.h"#ifdef ANDROID#include<android/log.h>#endif#ifdef DEBUG#include"nsThreadManager.h"#endif#include"Navigator.h"#include"Principal.h"#include"RuntimeService.h"#include"ScriptLoader.h"#include"ServiceWorkerEvents.h"#include"ServiceWorkerManager.h"#include"SharedWorker.h"#include"WorkerDebuggerManager.h"#include"WorkerHolder.h"#include"WorkerNavigator.h"#include"WorkerRunnable.h"#include"WorkerScope.h"#include"WorkerThread.h"// JS_MaybeGC will run once every second during normal execution.#define PERIODIC_GC_TIMER_DELAY_SEC 1// A shrinking GC will run five seconds after the last event is processed.#define IDLE_GC_TIMER_DELAY_SEC 5#define PREF_WORKERS_ENABLED "dom.workers.enabled"staticmozilla::LazyLogModulesWorkerPrivateLog("WorkerPrivate");staticmozilla::LazyLogModulesWorkerTimeoutsLog("WorkerTimeouts");mozilla::LogModule*WorkerLog(){returnsWorkerPrivateLog;}mozilla::LogModule*TimeoutsLog(){returnsWorkerTimeoutsLog;}#define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);usingnamespacemozilla;usingnamespacemozilla::dom;usingnamespacemozilla::ipc;USING_WORKERS_NAMESPACEMOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)#ifdef DEBUGBEGIN_WORKERS_NAMESPACEvoidAssertIsOnMainThread(){MOZ_ASSERT(NS_IsMainThread(),"Wrong thread!");}END_WORKERS_NAMESPACE#endifnamespace{#ifdef DEBUGconstnsIIDkDEBUGWorkerEventTargetIID={0xccaba3fa,0x5be2,0x4de2,{0xba,0x87,0x3b,0x3b,0x5b,0x1d,0x5,0xfb}};#endiftemplate<classT>classAutoPtrComparator{typedefnsAutoPtr<T>A;typedefT*B;public:boolEquals(constA&a,constB&b)const{returna&&b?*a==*b:!a&&!b?true:false;}boolLessThan(constA&a,constB&b)const{returna&&b?*a<*b:b?true:false;}};template<classT>inlineAutoPtrComparator<T>GetAutoPtrComparator(constnsTArray<nsAutoPtr<T>>&){returnAutoPtrComparator<T>();}// Specialize this if there's some class that has multiple nsISupports bases.template<classT>structISupportsBaseInfo{typedefTISupportsBase;};template<template<class>classSmartPtr,classT>inlinevoidSwapToISupportsArray(SmartPtr<T>&aSrc,nsTArray<nsCOMPtr<nsISupports>>&aDest){nsCOMPtr<nsISupports>*dest=aDest.AppendElement();T*raw=nullptr;aSrc.swap(raw);nsISupports*rawSupports=static_cast<typenameISupportsBaseInfo<T>::ISupportsBase*>(raw);dest->swap(rawSupports);}// This class is used to wrap any runnables that the worker receives via the// nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or// from the worker's EventTarget).classExternalRunnableWrapperfinal:publicWorkerRunnable{nsCOMPtr<nsIRunnable>mWrappedRunnable;public:ExternalRunnableWrapper(WorkerPrivate*aWorkerPrivate,nsIRunnable*aWrappedRunnable):WorkerRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mWrappedRunnable(aWrappedRunnable){MOZ_ASSERT(aWorkerPrivate);MOZ_ASSERT(aWrappedRunnable);}NS_DECL_ISUPPORTS_INHERITEDprivate:~ExternalRunnableWrapper(){}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{nsresultrv=mWrappedRunnable->Run();if(NS_FAILED(rv)){if(!JS_IsExceptionPending(aCx)){Throw(aCx,rv);}returnfalse;}returntrue;}nsresultCancel()override{nsresultrv;nsCOMPtr<nsICancelableRunnable>cancelable=do_QueryInterface(mWrappedRunnable);MOZ_ASSERT(cancelable);// We checked this earlier!rv=cancelable->Cancel();nsresultrv2=WorkerRunnable::Cancel();returnNS_FAILED(rv)?rv:rv2;}};structWindowAction{nsPIDOMWindowInner*mWindow;boolmDefaultAction;MOZ_IMPLICITWindowAction(nsPIDOMWindowInner*aWindow):mWindow(aWindow),mDefaultAction(true){}booloperator==(constWindowAction&aOther)const{returnmWindow==aOther.mWindow;}};voidLogErrorToConsole(constWorkerErrorReport&aReport,uint64_taInnerWindowId){AssertIsOnMainThread();RefPtr<nsScriptErrorBase>scriptError=newnsScriptError();NS_WARNING_ASSERTION(scriptError,"Failed to create script error!");if(scriptError){nsAutoCStringcategory("Web Worker");if(NS_FAILED(scriptError->InitWithWindowID(aReport.mMessage,aReport.mFilename,aReport.mLine,aReport.mLineNumber,aReport.mColumnNumber,aReport.mFlags,category,aInnerWindowId))){NS_WARNING("Failed to init script error!");scriptError=nullptr;}for(size_ti=0,len=aReport.mNotes.Length();i<len;i++){constWorkerErrorNote¬e=aReport.mNotes.ElementAt(i);nsScriptErrorNote*noteObject=newnsScriptErrorNote();noteObject->Init(note.mMessage,note.mFilename,note.mLineNumber,note.mColumnNumber);scriptError->AddNote(noteObject);}}nsCOMPtr<nsIConsoleService>consoleService=do_GetService(NS_CONSOLESERVICE_CONTRACTID);NS_WARNING_ASSERTION(consoleService,"Failed to get console service!");if(consoleService){if(scriptError){if(NS_SUCCEEDED(consoleService->LogMessage(scriptError))){return;}NS_WARNING("LogMessage failed!");}elseif(NS_SUCCEEDED(consoleService->LogStringMessage(aReport.mMessage.BeginReading()))){return;}NS_WARNING("LogStringMessage failed!");}NS_ConvertUTF16toUTF8msg(aReport.mMessage);NS_ConvertUTF16toUTF8filename(aReport.mFilename);staticconstcharkErrorString[]="JS error in Web Worker: %s [%s:%u]";#ifdef ANDROID__android_log_print(ANDROID_LOG_INFO,"Gecko",kErrorString,msg.get(),filename.get(),aReport.mLineNumber);#endiffprintf(stderr,kErrorString,msg.get(),filename.get(),aReport.mLineNumber);fflush(stderr);}classMainThreadReleaseRunnablefinal:publicRunnable{nsTArray<nsCOMPtr<nsISupports>>mDoomed;nsCOMPtr<nsILoadGroup>mLoadGroupToCancel;public:MainThreadReleaseRunnable(nsTArray<nsCOMPtr<nsISupports>>&aDoomed,nsCOMPtr<nsILoadGroup>&aLoadGroupToCancel):mozilla::Runnable("MainThreadReleaseRunnable"){mDoomed.SwapElements(aDoomed);mLoadGroupToCancel.swap(aLoadGroupToCancel);}NS_DECL_ISUPPORTS_INHERITEDNS_IMETHODRun()override{if(mLoadGroupToCancel){mLoadGroupToCancel->Cancel(NS_BINDING_ABORTED);mLoadGroupToCancel=nullptr;}mDoomed.Clear();returnNS_OK;}private:~MainThreadReleaseRunnable(){}};classWorkerFinishedRunnablefinal:publicWorkerControlRunnable{WorkerPrivate*mFinishedWorker;public:WorkerFinishedRunnable(WorkerPrivate*aWorkerPrivate,WorkerPrivate*aFinishedWorker):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mFinishedWorker(aFinishedWorker){}private:virtualboolPreDispatch(WorkerPrivate*aWorkerPrivate)override{// Silence bad assertions.returntrue;}virtualvoidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{// Silence bad assertions.}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{if(!mFinishedWorker->ProxyReleaseMainThreadObjects()){NS_WARNING("Failed to dispatch, going to leak!");}RuntimeService*runtime=RuntimeService::GetService();NS_ASSERTION(runtime,"This should never be null!");mFinishedWorker->DisableDebugger();runtime->UnregisterWorker(mFinishedWorker);mFinishedWorker->ClearSelfRef();returntrue;}};classTopLevelWorkerFinishedRunnablefinal:publicRunnable{WorkerPrivate*mFinishedWorker;public:explicitTopLevelWorkerFinishedRunnable(WorkerPrivate*aFinishedWorker):mozilla::Runnable("TopLevelWorkerFinishedRunnable"),mFinishedWorker(aFinishedWorker){aFinishedWorker->AssertIsOnWorkerThread();}NS_DECL_ISUPPORTS_INHERITEDprivate:~TopLevelWorkerFinishedRunnable(){}NS_IMETHODRun()override{AssertIsOnMainThread();RuntimeService*runtime=RuntimeService::GetService();MOZ_ASSERT(runtime);mFinishedWorker->DisableDebugger();runtime->UnregisterWorker(mFinishedWorker);if(!mFinishedWorker->ProxyReleaseMainThreadObjects()){NS_WARNING("Failed to dispatch, going to leak!");}mFinishedWorker->ClearSelfRef();returnNS_OK;}};classModifyBusyCountRunnablefinal:publicWorkerControlRunnable{boolmIncrease;public:ModifyBusyCountRunnable(WorkerPrivate*aWorkerPrivate,boolaIncrease):WorkerControlRunnable(aWorkerPrivate,ParentThreadUnchangedBusyCount),mIncrease(aIncrease){}private:virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{returnaWorkerPrivate->ModifyBusyCount(mIncrease);}virtualvoidPostRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate,boolaRunResult)override{if(mIncrease){WorkerControlRunnable::PostRun(aCx,aWorkerPrivate,aRunResult);return;}// Don't do anything here as it's possible that aWorkerPrivate has been// deleted.}};classReportCompileErrorRunnablefinal:publicWorkerRunnable{public:staticvoidCreateAndDispatch(JSContext*aCx,WorkerPrivate*aWorkerPrivate){MOZ_ASSERT(aWorkerPrivate);aWorkerPrivate->AssertIsOnWorkerThread();RefPtr<ReportCompileErrorRunnable>runnable=newReportCompileErrorRunnable(aCx,aWorkerPrivate);runnable->Dispatch();}private:ReportCompileErrorRunnable(JSContext*aCx,WorkerPrivate*aWorkerPrivate):WorkerRunnable(aWorkerPrivate,ParentThreadUnchangedBusyCount){aWorkerPrivate->AssertIsOnWorkerThread();}voidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{aWorkerPrivate->AssertIsOnWorkerThread();// Dispatch may fail if the worker was canceled, no need to report that as// an error, so don't call base class PostDispatch.}boolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{if(aWorkerPrivate->IsFrozen()||aWorkerPrivate->IsParentWindowPaused()){MOZ_ASSERT(!IsDebuggerRunnable());aWorkerPrivate->QueueRunnable(this);returntrue;}if(aWorkerPrivate->IsSharedWorker()){aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx,nullptr,/* isErrorEvent */false);returntrue;}if(aWorkerPrivate->IsServiceWorker()){RefPtr<ServiceWorkerManager>swm=ServiceWorkerManager::GetInstance();if(swm){swm->HandleError(aCx,aWorkerPrivate->GetPrincipal(),aWorkerPrivate->ServiceWorkerScope(),aWorkerPrivate->ScriptURL(),EmptyString(),EmptyString(),EmptyString(),0,0,JSREPORT_ERROR,JSEXN_ERR);}returntrue;}if(!aWorkerPrivate->IsAcceptingEvents()){returntrue;}RefPtr<Event>event=Event::Constructor(aWorkerPrivate,NS_LITERAL_STRING("error"),EventInit());event->SetTrusted(true);nsEventStatusstatus=nsEventStatus_eIgnore;aWorkerPrivate->DispatchDOMEvent(nullptr,event,nullptr,&status);returntrue;}};classCompileScriptRunnablefinal:publicWorkerRunnable{nsStringmScriptURL;public:explicitCompileScriptRunnable(WorkerPrivate*aWorkerPrivate,constnsAString&aScriptURL):WorkerRunnable(aWorkerPrivate),mScriptURL(aScriptURL){}private:// We can't implement PreRun effectively, because at the point when that would// run we have not yet done our load so don't know things like our final// principal and whatnot.virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->AssertIsOnWorkerThread();ErrorResultrv;scriptloader::LoadMainScript(aWorkerPrivate,mScriptURL,WorkerScript,rv);rv.WouldReportJSException();// Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still// return false and don't SetWorkerScriptExecutedSuccessfully() in that// case, but don't throw anything on aCx. The idea is to not dispatch error// events if our load is canceled with that error code.if(rv.ErrorCodeIs(NS_BINDING_ABORTED)){rv.SuppressException();returnfalse;}WorkerGlobalScope*globalScope=aWorkerPrivate->GlobalScope();if(NS_WARN_IF(!globalScope)){// We never got as far as calling GetOrCreateGlobalScope, or it failed.// We have no way to enter a compartment, hence no sane way to report this// error. :(rv.SuppressException();returnfalse;}// Make sure to propagate exceptions from rv onto aCx, so that they will get// reported after we return. We want to propagate just JS exceptions,// because all the other errors are handled when the script is loaded.// See: https://dom.spec.whatwg.org/#concept-event-fireif(rv.Failed()&&!rv.IsJSException()){ReportCompileErrorRunnable::CreateAndDispatch(aCx,aWorkerPrivate);rv.SuppressException();returnfalse;}// This is a little dumb, but aCx is in the null compartment here because we// set it up that way in our Run(), since we had not created the global at// that point yet. So we need to enter the compartment of our global,// because setting a pending exception on aCx involves wrapping into its// current compartment. Luckily we have a global now.JSAutoCompartmentac(aCx,globalScope->GetGlobalJSObject());if(rv.MaybeSetPendingException(aCx)){returnfalse;}aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();returntrue;}};classCompileDebuggerScriptRunnablefinal:publicWorkerDebuggerRunnable{nsStringmScriptURL;public:CompileDebuggerScriptRunnable(WorkerPrivate*aWorkerPrivate,constnsAString&aScriptURL):WorkerDebuggerRunnable(aWorkerPrivate),mScriptURL(aScriptURL){}private:virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->AssertIsOnWorkerThread();WorkerDebuggerGlobalScope*globalScope=aWorkerPrivate->CreateDebuggerGlobalScope(aCx);if(!globalScope){NS_WARNING("Failed to make global!");returnfalse;}JS::Rooted<JSObject*>global(aCx,globalScope->GetWrapper());ErrorResultrv;JSAutoCompartmentac(aCx,global);scriptloader::LoadMainScript(aWorkerPrivate,mScriptURL,DebuggerScript,rv);rv.WouldReportJSException();// Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still// return false and don't SetWorkerScriptExecutedSuccessfully() in that// case, but don't throw anything on aCx. The idea is to not dispatch error// events if our load is canceled with that error code.if(rv.ErrorCodeIs(NS_BINDING_ABORTED)){rv.SuppressException();returnfalse;}// Make sure to propagate exceptions from rv onto aCx, so that they will get// reported after we return. We do this for all failures on rv, because now// we're using rv to track all the state we care about.if(rv.MaybeSetPendingException(aCx)){returnfalse;}returntrue;}};classMessageEventRunnablefinal:publicWorkerRunnable,publicStructuredCloneHolder{public:MessageEventRunnable(WorkerPrivate*aWorkerPrivate,TargetAndBusyBehavioraBehavior):WorkerRunnable(aWorkerPrivate,aBehavior),StructuredCloneHolder(CloningSupported,TransferringSupported,StructuredCloneScope::SameProcessDifferentThread){}boolDispatchDOMEvent(JSContext*aCx,WorkerPrivate*aWorkerPrivate,DOMEventTargetHelper*aTarget,boolaIsMainThread){nsCOMPtr<nsIGlobalObject>parent=do_QueryInterface(aTarget->GetParentObject());// For some workers without window, parent is null and we try to find it// from the JS Context.if(!parent){JS::Rooted<JSObject*>globalObject(aCx,JS::CurrentGlobalOrNull(aCx));if(NS_WARN_IF(!globalObject)){returnfalse;}parent=xpc::NativeGlobal(globalObject);if(NS_WARN_IF(!parent)){returnfalse;}}MOZ_ASSERT(parent);JS::Rooted<JS::Value>messageData(aCx);ErrorResultrv;UniquePtr<AbstractTimelineMarker>start;UniquePtr<AbstractTimelineMarker>end;RefPtr<TimelineConsumers>timelines=TimelineConsumers::Get();boolisTimelineRecording=timelines&&!timelines->IsEmpty();if(isTimelineRecording){start=MakeUnique<WorkerTimelineMarker>(aIsMainThread?ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread:ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,MarkerTracingType::START);}Read(parent,aCx,&messageData,rv);if(isTimelineRecording){end=MakeUnique<WorkerTimelineMarker>(aIsMainThread?ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread:ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,MarkerTracingType::END);timelines->AddMarkerForAllObservedDocShells(start);timelines->AddMarkerForAllObservedDocShells(end);}if(NS_WARN_IF(rv.Failed())){xpc::Throw(aCx,rv.StealNSResult());returnfalse;}Sequence<OwningNonNull<MessagePort>>ports;if(!TakeTransferredPortsAsSequence(ports)){returnfalse;}nsCOMPtr<nsIDOMEvent>domEvent;RefPtr<MessageEvent>event=newMessageEvent(aTarget,nullptr,nullptr);event->InitMessageEvent(nullptr,NS_LITERAL_STRING("message"),false/* non-bubbling */,false/* cancelable */,messageData,EmptyString(),EmptyString(),nullptr,ports);domEvent=do_QueryObject(event);domEvent->SetTrusted(true);nsEventStatusdummy=nsEventStatus_eIgnore;aTarget->DispatchDOMEvent(nullptr,domEvent,nullptr,&dummy);returntrue;}private:virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{if(mBehavior==ParentThreadUnchangedBusyCount){// Don't fire this event if the JS object has been disconnected from the// private object.if(!aWorkerPrivate->IsAcceptingEvents()){returntrue;}if(aWorkerPrivate->IsFrozen()||aWorkerPrivate->IsParentWindowPaused()){MOZ_ASSERT(!IsDebuggerRunnable());aWorkerPrivate->QueueRunnable(this);returntrue;}aWorkerPrivate->AssertInnerWindowIsCorrect();returnDispatchDOMEvent(aCx,aWorkerPrivate,aWorkerPrivate,!aWorkerPrivate->GetParent());}MOZ_ASSERT(aWorkerPrivate==GetWorkerPrivateFromContext(aCx));returnDispatchDOMEvent(aCx,aWorkerPrivate,aWorkerPrivate->GlobalScope(),false);}};classDebuggerMessageEventRunnable:publicWorkerDebuggerRunnable{nsStringmMessage;public:DebuggerMessageEventRunnable(WorkerPrivate*aWorkerPrivate,constnsAString&aMessage):WorkerDebuggerRunnable(aWorkerPrivate),mMessage(aMessage){}private:virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{WorkerDebuggerGlobalScope*globalScope=aWorkerPrivate->DebuggerGlobalScope();MOZ_ASSERT(globalScope);JS::Rooted<JSString*>message(aCx,JS_NewUCStringCopyN(aCx,mMessage.get(),mMessage.Length()));if(!message){returnfalse;}JS::Rooted<JS::Value>data(aCx,JS::StringValue(message));RefPtr<MessageEvent>event=newMessageEvent(globalScope,nullptr,nullptr);event->InitMessageEvent(nullptr,NS_LITERAL_STRING("message"),false,// canBubbletrue,// cancelabledata,EmptyString(),EmptyString(),nullptr,Sequence<OwningNonNull<MessagePort>>());event->SetTrusted(true);nsCOMPtr<nsIDOMEvent>domEvent=do_QueryObject(event);nsEventStatusstatus=nsEventStatus_eIgnore;globalScope->DispatchDOMEvent(nullptr,domEvent,nullptr,&status);returntrue;}};classNotifyRunnablefinal:publicWorkerControlRunnable{StatusmStatus;public:NotifyRunnable(WorkerPrivate*aWorkerPrivate,StatusaStatus):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mStatus(aStatus){MOZ_ASSERT(aStatus==Closing||aStatus==Terminating||aStatus==Canceling||aStatus==Killing);}private:virtualboolPreDispatch(WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->AssertIsOnParentThread();returnaWorkerPrivate->ModifyBusyCount(true);}virtualvoidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{aWorkerPrivate->AssertIsOnParentThread();if(!aDispatchResult){// We couldn't dispatch to the worker, which means it's already dead.// Undo the busy count modification.aWorkerPrivate->ModifyBusyCount(false);}}virtualvoidPostRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate,boolaRunResult)override{aWorkerPrivate->ModifyBusyCountFromWorker(false);return;}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{boolok=aWorkerPrivate->NotifyInternal(aCx,mStatus);MOZ_ASSERT(!JS_IsExceptionPending(aCx));returnok;}};classFreezeRunnablefinal:publicWorkerControlRunnable{public:explicitFreezeRunnable(WorkerPrivate*aWorkerPrivate):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount){}private:virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{returnaWorkerPrivate->FreezeInternal();}};classThawRunnablefinal:publicWorkerControlRunnable{public:explicitThawRunnable(WorkerPrivate*aWorkerPrivate):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount){}private:virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{returnaWorkerPrivate->ThawInternal();}};classReportErrorToConsoleRunnablefinal:publicWorkerRunnable{constchar*mMessage;public:// aWorkerPrivate is the worker thread we're on (or the main thread, if null)staticvoidReport(WorkerPrivate*aWorkerPrivate,constchar*aMessage){if(aWorkerPrivate){aWorkerPrivate->AssertIsOnWorkerThread();}else{AssertIsOnMainThread();}// Now fire a runnable to do the same on the parent's thread if we can.if(aWorkerPrivate){RefPtr<ReportErrorToConsoleRunnable>runnable=newReportErrorToConsoleRunnable(aWorkerPrivate,aMessage);runnable->Dispatch();return;}// Log a warning to the console.nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,NS_LITERAL_CSTRING("DOM"),nullptr,nsContentUtils::eDOM_PROPERTIES,aMessage);}private:ReportErrorToConsoleRunnable(WorkerPrivate*aWorkerPrivate,constchar*aMessage):WorkerRunnable(aWorkerPrivate,ParentThreadUnchangedBusyCount),mMessage(aMessage){}virtualvoidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{aWorkerPrivate->AssertIsOnWorkerThread();// Dispatch may fail if the worker was canceled, no need to report that as// an error, so don't call base class PostDispatch.}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{WorkerPrivate*parent=aWorkerPrivate->GetParent();MOZ_ASSERT_IF(!parent,NS_IsMainThread());Report(parent,mMessage);returntrue;}};classReportErrorRunnablefinal:publicWorkerRunnable{WorkerErrorReportmReport;public:// aWorkerPrivate is the worker thread we're on (or the main thread, if null)// aTarget is the worker object that we are going to fire an error at// (if any).staticvoidReportError(JSContext*aCx,WorkerPrivate*aWorkerPrivate,boolaFireAtScope,WorkerPrivate*aTarget,constWorkerErrorReport&aReport,uint64_taInnerWindowId,JS::Handle<JS::Value>aException=JS::NullHandleValue){if(aWorkerPrivate){aWorkerPrivate->AssertIsOnWorkerThread();}else{AssertIsOnMainThread();}// We should not fire error events for warnings but instead make sure that// they show up in the error console.if(!JSREPORT_IS_WARNING(aReport.mFlags)){// First fire an ErrorEvent at the worker.RootedDictionary<ErrorEventInit>init(aCx);if(aReport.mMutedError){init.mMessage.AssignLiteral("Script error.");}else{init.mMessage=aReport.mMessage;init.mFilename=aReport.mFilename;init.mLineno=aReport.mLineNumber;init.mError=aException;}init.mCancelable=true;init.mBubbles=false;if(aTarget){RefPtr<ErrorEvent>event=ErrorEvent::Constructor(aTarget,NS_LITERAL_STRING("error"),init);event->SetTrusted(true);nsEventStatusstatus=nsEventStatus_eIgnore;aTarget->DispatchDOMEvent(nullptr,event,nullptr,&status);if(status==nsEventStatus_eConsumeNoDefault){return;}}// Now fire an event at the global object, but don't do that if the error// code is too much recursion and this is the same script threw the error.// XXXbz the interaction of this with worker errors seems kinda broken.// An overrecursion in the debugger or debugger sandbox will get turned// into an error event on our parent worker!// https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks making this// better.if(aFireAtScope&&(aTarget||aReport.mErrorNumber!=JSMSG_OVER_RECURSED)){JS::Rooted<JSObject*>global(aCx,JS::CurrentGlobalOrNull(aCx));NS_ASSERTION(global,"This should never be null!");nsEventStatusstatus=nsEventStatus_eIgnore;nsIScriptGlobalObject*sgo;if(aWorkerPrivate){WorkerGlobalScope*globalScope=nullptr;UNWRAP_OBJECT(WorkerGlobalScope,global,globalScope);if(!globalScope){WorkerDebuggerGlobalScope*globalScope=nullptr;UNWRAP_OBJECT(WorkerDebuggerGlobalScope,global,globalScope);MOZ_ASSERT_IF(globalScope,globalScope->GetWrapperPreserveColor()==global);if(globalScope||IsDebuggerSandbox(global)){aWorkerPrivate->ReportErrorToDebugger(aReport.mFilename,aReport.mLineNumber,aReport.mMessage);return;}MOZ_ASSERT(SimpleGlobalObject::SimpleGlobalType(global)==SimpleGlobalObject::GlobalType::BindingDetail);// XXXbz We should really log this to console, but unwinding out of// this stuff without ending up firing any events is ... hard. Just// return for now.// https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks// making this better.return;}MOZ_ASSERT(globalScope->GetWrapperPreserveColor()==global);nsIDOMEventTarget*target=static_cast<nsIDOMEventTarget*>(globalScope);RefPtr<ErrorEvent>event=ErrorEvent::Constructor(aTarget,NS_LITERAL_STRING("error"),init);event->SetTrusted(true);if(NS_FAILED(EventDispatcher::DispatchDOMEvent(target,nullptr,event,nullptr,&status))){NS_WARNING("Failed to dispatch worker thread error event!");status=nsEventStatus_eIgnore;}}elseif((sgo=nsJSUtils::GetStaticScriptGlobal(global))){MOZ_ASSERT(NS_IsMainThread());if(NS_FAILED(sgo->HandleScriptError(init,&status))){NS_WARNING("Failed to dispatch main thread error event!");status=nsEventStatus_eIgnore;}}// Was preventDefault() called?if(status==nsEventStatus_eConsumeNoDefault){return;}}}// Now fire a runnable to do the same on the parent's thread if we can.if(aWorkerPrivate){RefPtr<ReportErrorRunnable>runnable=newReportErrorRunnable(aWorkerPrivate,aReport);runnable->Dispatch();return;}// Otherwise log an error to the error console.LogErrorToConsole(aReport,aInnerWindowId);}private:ReportErrorRunnable(WorkerPrivate*aWorkerPrivate,constWorkerErrorReport&aReport):WorkerRunnable(aWorkerPrivate,ParentThreadUnchangedBusyCount),mReport(aReport){}virtualvoidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{aWorkerPrivate->AssertIsOnWorkerThread();// Dispatch may fail if the worker was canceled, no need to report that as// an error, so don't call base class PostDispatch.}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{JS::Rooted<JSObject*>target(aCx,aWorkerPrivate->GetWrapper());uint64_tinnerWindowId;boolfireAtScope=true;boolworkerIsAcceptingEvents=aWorkerPrivate->IsAcceptingEvents();WorkerPrivate*parent=aWorkerPrivate->GetParent();if(parent){innerWindowId=0;}else{AssertIsOnMainThread();if(aWorkerPrivate->IsFrozen()||aWorkerPrivate->IsParentWindowPaused()){MOZ_ASSERT(!IsDebuggerRunnable());aWorkerPrivate->QueueRunnable(this);returntrue;}if(aWorkerPrivate->IsSharedWorker()){aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx,&mReport,/* isErrorEvent */true);returntrue;}// Service workers do not have a main thread parent global, so normal// worker error reporting will crash. Instead, pass the error to// the ServiceWorkerManager to report on any controlled documents.if(aWorkerPrivate->IsServiceWorker()){RefPtr<ServiceWorkerManager>swm=ServiceWorkerManager::GetInstance();if(swm){swm->HandleError(aCx,aWorkerPrivate->GetPrincipal(),aWorkerPrivate->ServiceWorkerScope(),aWorkerPrivate->ScriptURL(),mReport.mMessage,mReport.mFilename,mReport.mLine,mReport.mLineNumber,mReport.mColumnNumber,mReport.mFlags,mReport.mExnType);}returntrue;}// The innerWindowId is only required if we are going to ReportError// below, which is gated on this condition. The inner window correctness// check is only going to succeed when the worker is accepting events.if(workerIsAcceptingEvents){aWorkerPrivate->AssertInnerWindowIsCorrect();innerWindowId=aWorkerPrivate->WindowID();}}// Don't fire this event if the JS object has been disconnected from the// private object.if(!workerIsAcceptingEvents){returntrue;}ReportError(aCx,parent,fireAtScope,aWorkerPrivate,mReport,innerWindowId);returntrue;}};classTimerRunnablefinal:publicWorkerRunnable,publicnsITimerCallback{public:NS_DECL_ISUPPORTS_INHERITEDexplicitTimerRunnable(WorkerPrivate*aWorkerPrivate):WorkerRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount){}private:~TimerRunnable(){}virtualboolPreDispatch(WorkerPrivate*aWorkerPrivate)override{// Silence bad assertions.returntrue;}virtualvoidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{// Silence bad assertions.}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{returnaWorkerPrivate->RunExpiredTimeouts(aCx);}NS_IMETHODNotify(nsITimer*aTimer)override{returnRun();}};NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable,WorkerRunnable,nsITimerCallback)classDebuggerImmediateRunnable:publicWorkerRunnable{RefPtr<dom::Function>mHandler;public:explicitDebuggerImmediateRunnable(WorkerPrivate*aWorkerPrivate,dom::Function&aHandler):WorkerRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mHandler(&aHandler){}private:virtualboolIsDebuggerRunnable()constoverride{returntrue;}virtualboolPreDispatch(WorkerPrivate*aWorkerPrivate)override{// Silence bad assertions.returntrue;}virtualvoidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{// Silence bad assertions.}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{JS::Rooted<JSObject*>global(aCx,JS::CurrentGlobalOrNull(aCx));JS::Rooted<JS::Value>callable(aCx,JS::ObjectOrNullValue(mHandler->CallableOrNull()));JS::HandleValueArrayargs=JS::HandleValueArray::empty();JS::Rooted<JS::Value>rval(aCx);if(!JS_CallFunctionValue(aCx,global,callable,args,&rval)){// Just return false; WorkerRunnable::Run will report the exception.returnfalse;}returntrue;}};voidPeriodicGCTimerCallback(nsITimer*aTimer,void*aClosure){autoworkerPrivate=static_cast<WorkerPrivate*>(aClosure);MOZ_DIAGNOSTIC_ASSERT(workerPrivate);workerPrivate->AssertIsOnWorkerThread();workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),false/* shrinking */,false/* collect children */);}voidIdleGCTimerCallback(nsITimer*aTimer,void*aClosure){autoworkerPrivate=static_cast<WorkerPrivate*>(aClosure);MOZ_DIAGNOSTIC_ASSERT(workerPrivate);workerPrivate->AssertIsOnWorkerThread();workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),true/* shrinking */,false/* collect children */);}classUpdateContextOptionsRunnablefinal:publicWorkerControlRunnable{JS::ContextOptionsmContextOptions;public:UpdateContextOptionsRunnable(WorkerPrivate*aWorkerPrivate,constJS::ContextOptions&aContextOptions):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mContextOptions(aContextOptions){}private:virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->UpdateContextOptionsInternal(aCx,mContextOptions);returntrue;}};classUpdatePreferenceRunnablefinal:publicWorkerControlRunnable{WorkerPreferencemPref;boolmValue;public:UpdatePreferenceRunnable(WorkerPrivate*aWorkerPrivate,WorkerPreferenceaPref,boolaValue):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mPref(aPref),mValue(aValue){}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->UpdatePreferenceInternal(mPref,mValue);returntrue;}};classUpdateLanguagesRunnablefinal:publicWorkerRunnable{nsTArray<nsString>mLanguages;public:UpdateLanguagesRunnable(WorkerPrivate*aWorkerPrivate,constnsTArray<nsString>&aLanguages):WorkerRunnable(aWorkerPrivate),mLanguages(aLanguages){}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->UpdateLanguagesInternal(mLanguages);returntrue;}};classUpdateJSWorkerMemoryParameterRunnablefinal:publicWorkerControlRunnable{uint32_tmValue;JSGCParamKeymKey;public:UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate*aWorkerPrivate,JSGCParamKeyaKey,uint32_taValue):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mValue(aValue),mKey(aKey){}private:virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx,mKey,mValue);returntrue;}};#ifdef JS_GC_ZEALclassUpdateGCZealRunnablefinal:publicWorkerControlRunnable{uint8_tmGCZeal;uint32_tmFrequency;public:UpdateGCZealRunnable(WorkerPrivate*aWorkerPrivate,uint8_taGCZeal,uint32_taFrequency):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mGCZeal(aGCZeal),mFrequency(aFrequency){}private:virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->UpdateGCZealInternal(aCx,mGCZeal,mFrequency);returntrue;}};#endifclassGarbageCollectRunnablefinal:publicWorkerControlRunnable{boolmShrinking;boolmCollectChildren;public:GarbageCollectRunnable(WorkerPrivate*aWorkerPrivate,boolaShrinking,boolaCollectChildren):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mShrinking(aShrinking),mCollectChildren(aCollectChildren){}private:virtualboolPreDispatch(WorkerPrivate*aWorkerPrivate)override{// Silence bad assertions, this can be dispatched from either the main// thread or the timer thread..returntrue;}virtualvoidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{// Silence bad assertions, this can be dispatched from either the main// thread or the timer thread..}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->GarbageCollectInternal(aCx,mShrinking,mCollectChildren);returntrue;}};classCycleCollectRunnable:publicWorkerControlRunnable{boolmCollectChildren;public:CycleCollectRunnable(WorkerPrivate*aWorkerPrivate,boolaCollectChildren):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mCollectChildren(aCollectChildren){}boolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate){aWorkerPrivate->CycleCollectInternal(mCollectChildren);returntrue;}};classOfflineStatusChangeRunnable:publicWorkerRunnable{public:OfflineStatusChangeRunnable(WorkerPrivate*aWorkerPrivate,boolaIsOffline):WorkerRunnable(aWorkerPrivate),mIsOffline(aIsOffline){}boolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate){aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);returntrue;}private:boolmIsOffline;};classMemoryPressureRunnable:publicWorkerControlRunnable{public:explicitMemoryPressureRunnable(WorkerPrivate*aWorkerPrivate):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount){}boolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate){aWorkerPrivate->MemoryPressureInternal();returntrue;}};#ifdef DEBUGstaticboolStartsWithExplicit(nsACString&s){returnStringBeginsWith(s,NS_LITERAL_CSTRING("explicit/"));}#endifclassMessagePortRunnablefinal:publicWorkerRunnable{MessagePortIdentifiermPortIdentifier;public:MessagePortRunnable(WorkerPrivate*aWorkerPrivate,MessagePort*aPort):WorkerRunnable(aWorkerPrivate){MOZ_ASSERT(aPort);// In order to move the port from one thread to another one, we have to// close and disentangle it. The output will be a MessagePortIdentifier that// will be used to recreate a new MessagePort on the other thread.aPort->CloneAndDisentangle(mPortIdentifier);}private:~MessagePortRunnable(){}virtualboolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{returnaWorkerPrivate->ConnectMessagePort(aCx,mPortIdentifier);}nsresultCancel()override{MessagePort::ForceClose(mPortIdentifier);returnWorkerRunnable::Cancel();}};PRThread*PRThreadFromThread(nsIThread*aThread){MOZ_ASSERT(aThread);PRThread*result;MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));MOZ_ASSERT(result);returnresult;}classSimpleWorkerHolderfinal:publicWorkerHolder{public:virtualboolNotify(StatusaStatus){returntrue;}};}/* anonymous namespace */NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable,Runnable)NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable,Runnable)namespace{classWrappedControlRunnablefinal:publicWorkerControlRunnable{nsCOMPtr<nsIRunnable>mInner;~WrappedControlRunnable(){}public:WrappedControlRunnable(WorkerPrivate*aWorkerPrivate,already_AddRefed<nsIRunnable>&&aInner):WorkerControlRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mInner(aInner){}virtualboolPreDispatch(WorkerPrivate*aWorkerPrivate)override{// Silence bad assertions, this can be dispatched from any thread.returntrue;}virtualvoidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{// Silence bad assertions, this can be dispatched from any thread.}boolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{mInner->Run();returntrue;}nsresultCancel()override{// First run the default cancelation codeWorkerControlRunnable::Cancel();// Attempt to cancel the inner runnable as wellnsCOMPtr<nsICancelableRunnable>cr=do_QueryInterface(mInner);if(cr){returncr->Cancel();}returnNS_OK;}};}// anonymous namespaceBEGIN_WORKERS_NAMESPACEclassWorkerControlEventTargetfinal:publicnsIEventTarget{mozilla::MutexmMutex;WorkerPrivate*mWorkerPrivate;~WorkerControlEventTarget()=default;public:explicitWorkerControlEventTarget(WorkerPrivate*aWorkerPrivate):mMutex("WorkerControlEventTarget"),mWorkerPrivate(aWorkerPrivate){MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate);}voidForgetWorkerPrivate(WorkerPrivate*aWorkerPrivate){MutexAutoLocklock(mMutex);MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate==aWorkerPrivate);mWorkerPrivate=nullptr;}NS_IMETHODDispatchFromScript(nsIRunnable*aRunnable,uint32_taFlags)override{nsCOMPtr<nsIRunnable>runnable(aRunnable);returnDispatch(runnable.forget(),aFlags);}NS_IMETHODDispatch(already_AddRefed<nsIRunnable>aRunnable,uint32_taFlags=NS_DISPATCH_NORMAL)override{MutexAutoLocklock(mMutex);if(!mWorkerPrivate){returnNS_ERROR_FAILURE;}RefPtr<WorkerControlRunnable>r=newWrappedControlRunnable(mWorkerPrivate,Move(aRunnable));if(!r->Dispatch()){returnNS_ERROR_FAILURE;}returnNS_OK;}NS_IMETHODDelayedDispatch(already_AddRefed<nsIRunnable>,uint32_t)override{returnNS_ERROR_NOT_IMPLEMENTED;}NS_IMETHOD_(bool)IsOnCurrentThreadInfallible()override{MutexAutoLocklock(mMutex);if(!mWorkerPrivate){returnfalse;}returnmWorkerPrivate->IsOnCurrentThread();}NS_IMETHODIsOnCurrentThread(bool*aIsOnCurrentThread)override{MOZ_ASSERT(aIsOnCurrentThread);*aIsOnCurrentThread=IsOnCurrentThreadInfallible();returnNS_OK;}NS_DECL_THREADSAFE_ISUPPORTS};NS_IMPL_ISUPPORTS(WorkerControlEventTarget,nsIEventTarget)END_WORKERS_NAMESPACEWorkerLoadInfo::WorkerLoadInfo():mLoadFlags(nsIRequest::LOAD_NORMAL),mWindowID(UINT64_MAX),mServiceWorkerID(0),mReferrerPolicy(net::RP_Unset),mFromWindow(false),mEvalAllowed(false),mReportCSPViolations(false),mXHRParamsAllowed(false),mPrincipalIsSystem(false),mStorageAllowed(false),mServiceWorkersTestingInWindow(false){MOZ_COUNT_CTOR(WorkerLoadInfo);}WorkerLoadInfo::~WorkerLoadInfo(){MOZ_COUNT_DTOR(WorkerLoadInfo);}voidWorkerLoadInfo::StealFrom(WorkerLoadInfo&aOther){MOZ_ASSERT(!mBaseURI);aOther.mBaseURI.swap(mBaseURI);MOZ_ASSERT(!mResolvedScriptURI);aOther.mResolvedScriptURI.swap(mResolvedScriptURI);MOZ_ASSERT(!mPrincipal);aOther.mPrincipal.swap(mPrincipal);MOZ_ASSERT(!mScriptContext);aOther.mScriptContext.swap(mScriptContext);MOZ_ASSERT(!mWindow);aOther.mWindow.swap(mWindow);MOZ_ASSERT(!mCSP);aOther.mCSP.swap(mCSP);MOZ_ASSERT(!mChannel);aOther.mChannel.swap(mChannel);MOZ_ASSERT(!mLoadGroup);aOther.mLoadGroup.swap(mLoadGroup);MOZ_ASSERT(!mLoadFailedAsyncRunnable);aOther.mLoadFailedAsyncRunnable.swap(mLoadFailedAsyncRunnable);MOZ_ASSERT(!mInterfaceRequestor);aOther.mInterfaceRequestor.swap(mInterfaceRequestor);MOZ_ASSERT(!mPrincipalInfo);mPrincipalInfo=aOther.mPrincipalInfo.forget();mDomain=aOther.mDomain;mOrigin=aOther.mOrigin;mServiceWorkerCacheName=aOther.mServiceWorkerCacheName;mLoadFlags=aOther.mLoadFlags;mWindowID=aOther.mWindowID;mServiceWorkerID=aOther.mServiceWorkerID;mReferrerPolicy=aOther.mReferrerPolicy;mFromWindow=aOther.mFromWindow;mEvalAllowed=aOther.mEvalAllowed;mReportCSPViolations=aOther.mReportCSPViolations;mXHRParamsAllowed=aOther.mXHRParamsAllowed;mPrincipalIsSystem=aOther.mPrincipalIsSystem;mStorageAllowed=aOther.mStorageAllowed;mServiceWorkersTestingInWindow=aOther.mServiceWorkersTestingInWindow;mOriginAttributes=aOther.mOriginAttributes;}nsresultWorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal*aPrincipal,nsILoadGroup*aLoadGroup){AssertIsOnMainThread();MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup,aPrincipal));mPrincipal=aPrincipal;mPrincipalIsSystem=nsContentUtils::IsSystemPrincipal(aPrincipal);nsresultrv=aPrincipal->GetCsp(getter_AddRefs(mCSP));NS_ENSURE_SUCCESS(rv,rv);if(mCSP){mCSP->GetAllowsEval(&mReportCSPViolations,&mEvalAllowed);// Set ReferrerPolicyboolhasReferrerPolicy=false;uint32_trp=mozilla::net::RP_Unset;rv=mCSP->GetReferrerPolicy(&rp,&hasReferrerPolicy);NS_ENSURE_SUCCESS(rv,rv);if(hasReferrerPolicy){mReferrerPolicy=static_cast<net::ReferrerPolicy>(rp);}}else{mEvalAllowed=true;mReportCSPViolations=false;}mLoadGroup=aLoadGroup;mPrincipalInfo=newPrincipalInfo();mOriginAttributes=nsContentUtils::GetOriginAttributes(aLoadGroup);rv=PrincipalToPrincipalInfo(aPrincipal,mPrincipalInfo);NS_ENSURE_SUCCESS(rv,rv);rv=nsContentUtils::GetUTFOrigin(aPrincipal,mOrigin);NS_ENSURE_SUCCESS(rv,rv);returnNS_OK;}nsresultWorkerLoadInfo::GetPrincipalAndLoadGroupFromChannel(nsIChannel*aChannel,nsIPrincipal**aPrincipalOut,nsILoadGroup**aLoadGroupOut){AssertIsOnMainThread();MOZ_DIAGNOSTIC_ASSERT(aChannel);MOZ_DIAGNOSTIC_ASSERT(aPrincipalOut);MOZ_DIAGNOSTIC_ASSERT(aLoadGroupOut);// Initial triggering principal should be setMOZ_DIAGNOSTIC_ASSERT(mPrincipal);nsIScriptSecurityManager*ssm=nsContentUtils::GetSecurityManager();MOZ_DIAGNOSTIC_ASSERT(ssm);nsCOMPtr<nsIPrincipal>channelPrincipal;nsresultrv=ssm->GetChannelResultPrincipal(aChannel,getter_AddRefs(channelPrincipal));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsILoadGroup>channelLoadGroup;rv=aChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));NS_ENSURE_SUCCESS(rv,rv);MOZ_ASSERT(channelLoadGroup);// If the load principal is the system principal then the channel// principal must also be the system principal (we do not allow chrome// code to create workers with non-chrome scripts, and if we ever decide// to change this we need to make sure we don't always set// mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise// this channel principal must be same origin with the load principal (we// check again here in case redirects changed the location of the script).if(nsContentUtils::IsSystemPrincipal(mPrincipal)){if(!nsContentUtils::IsSystemPrincipal(channelPrincipal)){nsCOMPtr<nsIURI>finalURI;rv=NS_GetFinalChannelURI(aChannel,getter_AddRefs(finalURI));NS_ENSURE_SUCCESS(rv,rv);// See if this is a resource URI. Since JSMs usually come from// resource:// URIs we're currently considering all URIs with the// URI_IS_UI_RESOURCE flag as valid for creating privileged workers.boolisResource;rv=NS_URIChainHasFlags(finalURI,nsIProtocolHandler::URI_IS_UI_RESOURCE,&isResource);NS_ENSURE_SUCCESS(rv,rv);if(isResource){// Assign the system principal to the resource:// worker only if it// was loaded from code using the system principal.channelPrincipal=mPrincipal;}else{returnNS_ERROR_DOM_BAD_URI;}}}// The principal can change, but it should still match the original// load group's appId and browser element flag.MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(channelLoadGroup,channelPrincipal));channelPrincipal.forget(aPrincipalOut);channelLoadGroup.forget(aLoadGroupOut);returnNS_OK;}nsresultWorkerLoadInfo::SetPrincipalFromChannel(nsIChannel*aChannel){AssertIsOnMainThread();nsCOMPtr<nsIPrincipal>principal;nsCOMPtr<nsILoadGroup>loadGroup;nsresultrv=GetPrincipalAndLoadGroupFromChannel(aChannel,getter_AddRefs(principal),getter_AddRefs(loadGroup));NS_ENSURE_SUCCESS(rv,rv);returnSetPrincipalOnMainThread(principal,loadGroup);}#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLEDboolWorkerLoadInfo::FinalChannelPrincipalIsValid(nsIChannel*aChannel){AssertIsOnMainThread();nsCOMPtr<nsIPrincipal>principal;nsCOMPtr<nsILoadGroup>loadGroup;nsresultrv=GetPrincipalAndLoadGroupFromChannel(aChannel,getter_AddRefs(principal),getter_AddRefs(loadGroup));NS_ENSURE_SUCCESS(rv,false);// Verify that the channel is still a null principal. We don't care// if these are the exact same null principal object, though. From// the worker's perspective its the same effect.if(principal->GetIsNullPrincipal()&&mPrincipal->GetIsNullPrincipal()){returntrue;}// Otherwise we require exact equality. Redirects can happen, but they// are not allowed to change our principal.if(principal->Equals(mPrincipal)){returntrue;}returnfalse;}boolWorkerLoadInfo::PrincipalIsValid()const{returnmPrincipal&&mPrincipalInfo&&mPrincipalInfo->type()!=PrincipalInfo::T__None&&mPrincipalInfo->type()<=PrincipalInfo::T__Last;}boolWorkerLoadInfo::PrincipalURIMatchesScriptURL(){AssertIsOnMainThread();nsAutoCStringscheme;nsresultrv=mBaseURI->GetScheme(scheme);NS_ENSURE_SUCCESS(rv,false);// A system principal must either be a blob URL or a resource JSM.if(mPrincipal->GetIsSystemPrincipal()){if(scheme==NS_LITERAL_CSTRING("blob")){returntrue;}boolisResource=false;nsresultrv=NS_URIChainHasFlags(mBaseURI,nsIProtocolHandler::URI_IS_UI_RESOURCE,&isResource);NS_ENSURE_SUCCESS(rv,false);returnisResource;}// A null principal can occur for a data URL worker script or a blob URL// worker script from a sandboxed iframe.if(mPrincipal->GetIsNullPrincipal()){returnscheme==NS_LITERAL_CSTRING("data")||scheme==NS_LITERAL_CSTRING("blob");}// The principal for a blob: URL worker script does not have a matching URL.// This is likely a bug in our referer setting logic, but exempt it for now.// This is another reason we should fix bug 1340694 so that referer does not// depend on the principal URI.if(scheme==NS_LITERAL_CSTRING("blob")){returntrue;}nsCOMPtr<nsIURI>principalURI;rv=mPrincipal->GetURI(getter_AddRefs(principalURI));NS_ENSURE_SUCCESS(rv,false);NS_ENSURE_TRUE(principalURI,false);boolequal=false;rv=principalURI->Equals(mBaseURI,&equal);NS_ENSURE_SUCCESS(rv,false);returnequal;}#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLEDboolWorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate*aWorkerPrivate){nsCOMPtr<nsILoadGroup>nullLoadGroup;returnProxyReleaseMainThreadObjects(aWorkerPrivate,nullLoadGroup);}boolWorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate*aWorkerPrivate,nsCOMPtr<nsILoadGroup>&aLoadGroupToCancel){staticconstuint32_tkDoomedCount=10;nsTArray<nsCOMPtr<nsISupports>>doomed(kDoomedCount);SwapToISupportsArray(mWindow,doomed);SwapToISupportsArray(mScriptContext,doomed);SwapToISupportsArray(mBaseURI,doomed);SwapToISupportsArray(mResolvedScriptURI,doomed);SwapToISupportsArray(mPrincipal,doomed);SwapToISupportsArray(mChannel,doomed);SwapToISupportsArray(mCSP,doomed);SwapToISupportsArray(mLoadGroup,doomed);SwapToISupportsArray(mLoadFailedAsyncRunnable,doomed);SwapToISupportsArray(mInterfaceRequestor,doomed);// Before adding anything here update kDoomedCount above!MOZ_ASSERT(doomed.Length()==kDoomedCount);RefPtr<MainThreadReleaseRunnable>runnable=newMainThreadReleaseRunnable(doomed,aLoadGroupToCancel);returnNS_SUCCEEDED(aWorkerPrivate->DispatchToMainThread(runnable.forget()));}template<classDerived>classWorkerPrivateParent<Derived>::EventTargetfinal:publicnsISerialEventTarget{// This mutex protects mWorkerPrivate and must be acquired *before* the// WorkerPrivate's mutex whenever they must both be held.mozilla::MutexmMutex;WorkerPrivate*mWorkerPrivate;nsIEventTarget*mWeakNestedEventTarget;nsCOMPtr<nsIEventTarget>mNestedEventTarget;public:explicitEventTarget(WorkerPrivate*aWorkerPrivate):mMutex("WorkerPrivateParent::EventTarget::mMutex"),mWorkerPrivate(aWorkerPrivate),mWeakNestedEventTarget(nullptr){MOZ_ASSERT(aWorkerPrivate);}EventTarget(WorkerPrivate*aWorkerPrivate,nsIEventTarget*aNestedEventTarget):mMutex("WorkerPrivateParent::EventTarget::mMutex"),mWorkerPrivate(aWorkerPrivate),mWeakNestedEventTarget(aNestedEventTarget),mNestedEventTarget(aNestedEventTarget){MOZ_ASSERT(aWorkerPrivate);MOZ_ASSERT(aNestedEventTarget);}voidDisable(){nsCOMPtr<nsIEventTarget>nestedEventTarget;{MutexAutoLocklock(mMutex);// Note, Disable() can be called more than once safely.mWorkerPrivate=nullptr;mNestedEventTarget.swap(nestedEventTarget);}}nsIEventTarget*GetWeakNestedEventTarget()const{MOZ_ASSERT(mWeakNestedEventTarget);returnmWeakNestedEventTarget;}NS_DECL_THREADSAFE_ISUPPORTSNS_DECL_NSIEVENTTARGET_FULLprivate:~EventTarget(){}};WorkerLoadInfo::InterfaceRequestor::InterfaceRequestor(nsIPrincipal*aPrincipal,nsILoadGroup*aLoadGroup){MOZ_ASSERT(NS_IsMainThread());MOZ_ASSERT(aPrincipal);// Look for an existing LoadContext. This is optional and it's ok if// we don't find one.nsCOMPtr<nsILoadContext>baseContext;if(aLoadGroup){nsCOMPtr<nsIInterfaceRequestor>callbacks;aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));if(callbacks){callbacks->GetInterface(NS_GET_IID(nsILoadContext),getter_AddRefs(baseContext));}mOuterRequestor=callbacks;}mLoadContext=newLoadContext(aPrincipal,baseContext);}voidWorkerLoadInfo::InterfaceRequestor::MaybeAddTabChild(nsILoadGroup*aLoadGroup){MOZ_ASSERT(NS_IsMainThread());if(!aLoadGroup){return;}nsCOMPtr<nsIInterfaceRequestor>callbacks;aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));if(!callbacks){return;}nsCOMPtr<nsITabChild>tabChild;callbacks->GetInterface(NS_GET_IID(nsITabChild),getter_AddRefs(tabChild));if(!tabChild){return;}// Use weak references to the tab child. Holding a strong reference will// not prevent an ActorDestroy() from being called on the TabChild.// Therefore, we should let the TabChild destroy itself as soon as possible.mTabChildList.AppendElement(do_GetWeakReference(tabChild));}NS_IMETHODIMPWorkerLoadInfo::InterfaceRequestor::GetInterface(constnsIID&aIID,void**aSink){MOZ_ASSERT(NS_IsMainThread());MOZ_ASSERT(mLoadContext);if(aIID.Equals(NS_GET_IID(nsILoadContext))){nsCOMPtr<nsILoadContext>ref=mLoadContext;ref.forget(aSink);returnNS_OK;}// If we still have an active nsITabChild, then return it. Its possible,// though, that all of the TabChild objects have been destroyed. In that// case we return NS_NOINTERFACE.if(aIID.Equals(NS_GET_IID(nsITabChild))){nsCOMPtr<nsITabChild>tabChild=GetAnyLiveTabChild();if(!tabChild){returnNS_NOINTERFACE;}tabChild.forget(aSink);returnNS_OK;}if(aIID.Equals(NS_GET_IID(nsINetworkInterceptController))&&mOuterRequestor){// If asked for the network intercept controller, ask the outer requestor,// which could be the docshell.returnmOuterRequestor->GetInterface(aIID,aSink);}returnNS_NOINTERFACE;}already_AddRefed<nsITabChild>WorkerLoadInfo::InterfaceRequestor::GetAnyLiveTabChild(){MOZ_ASSERT(NS_IsMainThread());// Search our list of known TabChild objects for one that still exists.while(!mTabChildList.IsEmpty()){nsCOMPtr<nsITabChild>tabChild=do_QueryReferent(mTabChildList.LastElement());// Does this tab child still exist? If so, return it. We are done. If the// PBrowser actor is no longer useful, don't bother returning this tab.if(tabChild&&!static_cast<TabChild*>(tabChild.get())->IsDestroyed()){returntabChild.forget();}// Otherwise remove the stale weak reference and check the next onemTabChildList.RemoveElementAt(mTabChildList.Length()-1);}returnnullptr;}NS_IMPL_ADDREF(WorkerLoadInfo::InterfaceRequestor)NS_IMPL_RELEASE(WorkerLoadInfo::InterfaceRequestor)NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor,nsIInterfaceRequestor)structWorkerPrivate::TimeoutInfo{TimeoutInfo():mId(0),mIsInterval(false),mCanceled(false){MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);}~TimeoutInfo(){MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);}booloperator==(constTimeoutInfo&aOther){returnmTargetTime==aOther.mTargetTime;}booloperator<(constTimeoutInfo&aOther){returnmTargetTime<aOther.mTargetTime;}nsCOMPtr<nsIScriptTimeoutHandler>mHandler;mozilla::TimeStampmTargetTime;mozilla::TimeDurationmInterval;int32_tmId;boolmIsInterval;boolmCanceled;};classWorkerJSContextStatsfinal:publicJS::RuntimeStats{constnsCStringmRtPath;public:explicitWorkerJSContextStats(constnsACString&aRtPath):JS::RuntimeStats(JsWorkerMallocSizeOf),mRtPath(aRtPath){}~WorkerJSContextStats(){for(size_ti=0;i!=zoneStatsVector.length();i++){deletestatic_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);}for(size_ti=0;i!=compartmentStatsVector.length();i++){deletestatic_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);}}constnsCString&Path()const{returnmRtPath;}virtualvoidinitExtraZoneStats(JS::Zone*aZone,JS::ZoneStats*aZoneStats)override{MOZ_ASSERT(!aZoneStats->extra);// ReportJSRuntimeExplicitTreeStats expects that// aZoneStats->extra is a xpc::ZoneStatsExtras pointer.xpc::ZoneStatsExtras*extras=newxpc::ZoneStatsExtras;extras->pathPrefix=mRtPath;extras->pathPrefix+=nsPrintfCString("zone(0x%p)/",(void*)aZone);MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));aZoneStats->extra=extras;}virtualvoidinitExtraCompartmentStats(JSCompartment*aCompartment,JS::CompartmentStats*aCompartmentStats)override{MOZ_ASSERT(!aCompartmentStats->extra);// ReportJSRuntimeExplicitTreeStats expects that// aCompartmentStats->extra is a xpc::CompartmentStatsExtras pointer.xpc::CompartmentStatsExtras*extras=newxpc::CompartmentStatsExtras;// This is the |jsPathPrefix|. Each worker has exactly two compartments:// one for atoms, and one for everything else.extras->jsPathPrefix.Assign(mRtPath);extras->jsPathPrefix+=nsPrintfCString("zone(0x%p)/",(void*)js::GetCompartmentZone(aCompartment));extras->jsPathPrefix+=js::IsAtomsCompartment(aCompartment)?NS_LITERAL_CSTRING("compartment(web-worker-atoms)/"):NS_LITERAL_CSTRING("compartment(web-worker)/");// This should never be used when reporting with workers (hence the "?!").extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));extras->location=nullptr;aCompartmentStats->extra=extras;}};classWorkerPrivate::MemoryReporterfinal:publicnsIMemoryReporter{NS_DECL_THREADSAFE_ISUPPORTSfriendclassWorkerPrivate;SharedMutexmMutex;WorkerPrivate*mWorkerPrivate;boolmAlreadyMappedToAddon;public:explicitMemoryReporter(WorkerPrivate*aWorkerPrivate):mMutex(aWorkerPrivate->mMutex),mWorkerPrivate(aWorkerPrivate),mAlreadyMappedToAddon(false){aWorkerPrivate->AssertIsOnWorkerThread();}NS_IMETHODCollectReports(nsIHandleReportCallback*aHandleReport,nsISupports*aData,boolaAnonymize)override;private:classFinishCollectRunnable;classCollectReportsRunnablefinal:publicMainThreadWorkerControlRunnable{RefPtr<FinishCollectRunnable>mFinishCollectRunnable;constboolmAnonymize;public:CollectReportsRunnable(WorkerPrivate*aWorkerPrivate,nsIHandleReportCallback*aHandleReport,nsISupports*aHandlerData,boolaAnonymize,constnsACString&aPath);private:boolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override;~CollectReportsRunnable(){if(NS_IsMainThread()){mFinishCollectRunnable->Run();return;}WorkerPrivate*workerPrivate=GetCurrentThreadWorkerPrivate();MOZ_ASSERT(workerPrivate);MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(mFinishCollectRunnable.forget()));}};classFinishCollectRunnablefinal:publicRunnable{nsCOMPtr<nsIHandleReportCallback>mHandleReport;nsCOMPtr<nsISupports>mHandlerData;constboolmAnonymize;boolmSuccess;public:WorkerJSContextStatsmCxStats;explicitFinishCollectRunnable(nsIHandleReportCallback*aHandleReport,nsISupports*aHandlerData,boolaAnonymize,constnsACString&aPath);NS_IMETHODRun()override;voidSetSuccess(boolsuccess){mSuccess=success;}private:~FinishCollectRunnable(){// mHandleReport and mHandlerData are released on the main thread.AssertIsOnMainThread();}FinishCollectRunnable(constFinishCollectRunnable&)=delete;FinishCollectRunnable&operator=(constFinishCollectRunnable&)=delete;FinishCollectRunnable&operator=(constFinishCollectRunnable&&)=delete;};~MemoryReporter(){}voidDisable(){// Called from WorkerPrivate::DisableMemoryReporter.mMutex.AssertCurrentThreadOwns();NS_ASSERTION(mWorkerPrivate,"Disabled more than once!");mWorkerPrivate=nullptr;}// Only call this from the main thread and under mMutex lock.voidTryToMapAddon(nsACString&path);};NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter,nsIMemoryReporter)NS_IMETHODIMPWorkerPrivate::MemoryReporter::CollectReports(nsIHandleReportCallback*aHandleReport,nsISupports*aData,boolaAnonymize){AssertIsOnMainThread();RefPtr<CollectReportsRunnable>runnable;{MutexAutoLocklock(mMutex);if(!mWorkerPrivate){// This will effectively report 0 memory.nsCOMPtr<nsIMemoryReporterManager>manager=do_GetService("@mozilla.org/memory-reporter-manager;1");if(manager){manager->EndReport();}returnNS_OK;}nsAutoCStringpath;path.AppendLiteral("explicit/workers/workers(");if(aAnonymize&&!mWorkerPrivate->Domain().IsEmpty()){path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");}else{nsAutoCStringescapedDomain(mWorkerPrivate->Domain());if(escapedDomain.IsEmpty()){escapedDomain+="chrome";}else{escapedDomain.ReplaceChar('/','\\');}path.Append(escapedDomain);path.AppendLiteral(")/worker(");NS_ConvertUTF16toUTF8escapedURL(mWorkerPrivate->ScriptURL());escapedURL.ReplaceChar('/','\\');path.Append(escapedURL);}path.AppendPrintf(", 0x%p)/",static_cast<void*>(mWorkerPrivate));TryToMapAddon(path);runnable=newCollectReportsRunnable(mWorkerPrivate,aHandleReport,aData,aAnonymize,path);}if(!runnable->Dispatch()){returnNS_ERROR_UNEXPECTED;}returnNS_OK;}voidWorkerPrivate::MemoryReporter::TryToMapAddon(nsACString&path){AssertIsOnMainThread();mMutex.AssertCurrentThreadOwns();if(mAlreadyMappedToAddon||!mWorkerPrivate){return;}nsCOMPtr<nsIURI>scriptURI;if(NS_FAILED(NS_NewURI(getter_AddRefs(scriptURI),mWorkerPrivate->ScriptURL()))){return;}mAlreadyMappedToAddon=true;if(!XRE_IsParentProcess()){// Only try to access the service from the main process.return;}nsAutoCStringaddonId;boolok;nsCOMPtr<amIAddonManager>addonManager=do_GetService("@mozilla.org/addons/integration;1");if(!addonManager||NS_FAILED(addonManager->MapURIToAddonID(scriptURI,addonId,&ok))||!ok){return;}staticconstsize_texplicitLength=strlen("explicit/");addonId.Insert(NS_LITERAL_CSTRING("add-ons/"),0);addonId+="/";path.Insert(addonId,explicitLength);}WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(WorkerPrivate*aWorkerPrivate,nsIHandleReportCallback*aHandleReport,nsISupports*aHandlerData,boolaAnonymize,constnsACString&aPath):MainThreadWorkerControlRunnable(aWorkerPrivate),mFinishCollectRunnable(newFinishCollectRunnable(aHandleReport,aHandlerData,aAnonymize,aPath)),mAnonymize(aAnonymize){}boolWorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate){aWorkerPrivate->AssertIsOnWorkerThread();mFinishCollectRunnable->SetSuccess(aWorkerPrivate->CollectRuntimeStats(&mFinishCollectRunnable->mCxStats,mAnonymize));returntrue;}WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(nsIHandleReportCallback*aHandleReport,nsISupports*aHandlerData,boolaAnonymize,constnsACString&aPath):mozilla::Runnable("dom::workers::WorkerPrivate::MemoryReporter::FinishCollectRunnable"),mHandleReport(aHandleReport),mHandlerData(aHandlerData),mAnonymize(aAnonymize),mSuccess(false),mCxStats(aPath){}NS_IMETHODIMPWorkerPrivate::MemoryReporter::FinishCollectRunnable::Run(){AssertIsOnMainThread();nsCOMPtr<nsIMemoryReporterManager>manager=do_GetService("@mozilla.org/memory-reporter-manager;1");if(!manager)returnNS_OK;if(mSuccess){xpc::ReportJSRuntimeExplicitTreeStats(mCxStats,mCxStats.Path(),mHandleReport,mHandlerData,mAnonymize);}manager->EndReport();returnNS_OK;}WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget*aEventTarget):mEventTarget(aEventTarget),mCompleted(false),mResult(false)#ifdef DEBUG,mHasRun(false)#endif{}template<classDerived>nsIDocument*WorkerPrivateParent<Derived>::GetDocument()const{AssertIsOnMainThread();if(mLoadInfo.mWindow){returnmLoadInfo.mWindow->GetExtantDoc();}// if we don't have a document, we should query the document// from the parent in case of a nested workerWorkerPrivate*parent=mParent;while(parent){if(parent->mLoadInfo.mWindow){returnparent->mLoadInfo.mWindow->GetExtantDoc();}parent=parent->GetParent();}// couldn't query a document, give up and return nullptrreturnnullptr;}template<classDerived>voidWorkerPrivateParent<Derived>::SetCSP(nsIContentSecurityPolicy*aCSP){AssertIsOnMainThread();if(!aCSP){return;}WorkerPrivate*self=ParentAsWorkerPrivate();aCSP->EnsureEventTarget(self->mMainThreadEventTarget);mLoadInfo.mCSP=aCSP;}template<classDerived>nsresultWorkerPrivateParent<Derived>::SetCSPFromHeaderValues(constnsACString&aCSPHeaderValue,constnsACString&aCSPReportOnlyHeaderValue){AssertIsOnMainThread();MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);NS_ConvertASCIItoUTF16cspHeaderValue(aCSPHeaderValue);NS_ConvertASCIItoUTF16cspROHeaderValue(aCSPReportOnlyHeaderValue);nsCOMPtr<nsIContentSecurityPolicy>csp;nsresultrv=mLoadInfo.mPrincipal->EnsureCSP(nullptr,getter_AddRefs(csp));if(!csp){returnNS_OK;}WorkerPrivate*self=ParentAsWorkerPrivate();csp->EnsureEventTarget(self->mMainThreadEventTarget);// If there's a CSP header, apply it.if(!cspHeaderValue.IsEmpty()){rv=CSP_AppendCSPFromHeader(csp,cspHeaderValue,false);NS_ENSURE_SUCCESS(rv,rv);}// If there's a report-only CSP header, apply it.if(!cspROHeaderValue.IsEmpty()){rv=CSP_AppendCSPFromHeader(csp,cspROHeaderValue,true);NS_ENSURE_SUCCESS(rv,rv);}// Set evalAllowed, default value is set in GetAllowsEvalboolevalAllowed=false;boolreportEvalViolations=false;rv=csp->GetAllowsEval(&reportEvalViolations,&evalAllowed);NS_ENSURE_SUCCESS(rv,rv);// Set ReferrerPolicy, default value is set in GetReferrerPolicyboolhasReferrerPolicy=false;uint32_trp=mozilla::net::RP_Unset;rv=csp->GetReferrerPolicy(&rp,&hasReferrerPolicy);NS_ENSURE_SUCCESS(rv,rv);mLoadInfo.mCSP=csp;mLoadInfo.mEvalAllowed=evalAllowed;mLoadInfo.mReportCSPViolations=reportEvalViolations;if(hasReferrerPolicy){mLoadInfo.mReferrerPolicy=static_cast<net::ReferrerPolicy>(rp);}returnNS_OK;}template<classDerived>voidWorkerPrivateParent<Derived>::SetReferrerPolicyFromHeaderValue(constnsACString&aReferrerPolicyHeaderValue){NS_ConvertUTF8toUTF16headerValue(aReferrerPolicyHeaderValue);if(headerValue.IsEmpty()){return;}net::ReferrerPolicypolicy=nsContentUtils::GetReferrerPolicyFromHeader(headerValue);if(policy==net::RP_Unset){return;}SetReferrerPolicy(policy);}// Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the// templates.template<classDerived>typenameWorkerPrivateParent<Derived>::cycleCollectionWorkerPrivateParent<Derived>::_cycleCollectorGlobal=WorkerPrivateParent<Derived>::cycleCollection();template<classDerived>WorkerPrivateParent<Derived>::WorkerPrivateParent(WorkerPrivate*aParent,constnsAString&aScriptURL,boolaIsChromeWorker,WorkerTypeaWorkerType,constnsAString&aWorkerName,constnsACString&aServiceWorkerScope,WorkerLoadInfo&aLoadInfo):mMutex("WorkerPrivateParent Mutex"),mCondVar(mMutex,"WorkerPrivateParent CondVar"),mParent(aParent),mScriptURL(aScriptURL),mWorkerName(aWorkerName),mServiceWorkerScope(aServiceWorkerScope),mLoadingWorkerScript(false),mBusyCount(0),mParentWindowPausedDepth(0),mParentStatus(Pending),mParentFrozen(false),mIsChromeWorker(aIsChromeWorker),mMainThreadObjectsForgotten(false),mIsSecureContext(false),mWorkerType(aWorkerType),mCreationTimeStamp(TimeStamp::Now()),mCreationTimeHighRes((double)PR_Now()/PR_USEC_PER_MSEC){MOZ_ASSERT_IF(!IsDedicatedWorker(),NS_IsMainThread());if(aLoadInfo.mWindow){AssertIsOnMainThread();MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(),"Should have inner window here!");BindToOwner(aLoadInfo.mWindow);}mLoadInfo.StealFrom(aLoadInfo);if(aParent){aParent->AssertIsOnWorkerThread();// Note that this copies our parent's secure context state into mJSSettings.aParent->CopyJSSettings(mJSSettings);// And manually set our mIsSecureContext, though it's not really relevant to// dedicated workers...mIsSecureContext=aParent->IsSecureContext();MOZ_ASSERT_IF(mIsChromeWorker,mIsSecureContext);MOZ_ASSERT(IsDedicatedWorker());if(aParent->mParentFrozen){Freeze(nullptr);}}else{AssertIsOnMainThread();RuntimeService::GetDefaultJSSettings(mJSSettings);// Our secure context state depends on the kind of worker we have.if(UsesSystemPrincipal()||IsServiceWorker()){mIsSecureContext=true;}elseif(mLoadInfo.mWindow){// Shared and dedicated workers both inherit the loading window's secure// context state. Shared workers then prevent windows with a different// secure context state from attaching to them.mIsSecureContext=mLoadInfo.mWindow->IsSecureContext();}else{MOZ_ASSERT_UNREACHABLE("non-chrome worker that is not a service worker ""that has no parent and no associated window");}if(mIsSecureContext){mJSSettings.chrome.compartmentOptions.creationOptions().setSecureContext(true);mJSSettings.content.compartmentOptions.creationOptions().setSecureContext(true);}// Our parent can get suspended after it initiates the async creation// of a new worker thread. In this case suspend the new worker as well.if(mLoadInfo.mWindow&&mLoadInfo.mWindow->IsSuspended()){ParentWindowPaused();}if(mLoadInfo.mWindow&&mLoadInfo.mWindow->IsFrozen()){Freeze(mLoadInfo.mWindow);}}}template<classDerived>WorkerPrivateParent<Derived>::~WorkerPrivateParent(){DropJSObjects(this);}template<classDerived>JSObject*WorkerPrivateParent<Derived>::WrapObject(JSContext*aCx,JS::Handle<JSObject*>aGivenProto){MOZ_ASSERT(!IsSharedWorker(),"We should never wrap a WorkerPrivate for a SharedWorker");AssertIsOnParentThread();// XXXkhuey this should not need to be rooted, the analysis is dumb.// See bug 980181.JS::Rooted<JSObject*>wrapper(aCx,WorkerBinding::Wrap(aCx,ParentAsWorkerPrivate(),aGivenProto));if(wrapper){MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));}returnwrapper;}template<classDerived>nsresultWorkerPrivateParent<Derived>::DispatchPrivate(already_AddRefed<WorkerRunnable>aRunnable,nsIEventTarget*aSyncLoopTarget){// May be called on any thread!RefPtr<WorkerRunnable>runnable(aRunnable);WorkerPrivate*self=ParentAsWorkerPrivate();{MutexAutoLocklock(mMutex);MOZ_ASSERT_IF(aSyncLoopTarget,self->mThread);if(!self->mThread){if(ParentStatus()==Pending||self->mStatus==Pending){mPreStartRunnables.AppendElement(runnable);returnNS_OK;}NS_WARNING("Using a worker event target after the thread has already""been released!");returnNS_ERROR_UNEXPECTED;}if(self->mStatus==Dead||(!aSyncLoopTarget&&ParentStatus()>Running)){NS_WARNING("A runnable was posted to a worker that is already shutting ""down!");returnNS_ERROR_UNEXPECTED;}nsresultrv;if(aSyncLoopTarget){rv=aSyncLoopTarget->Dispatch(runnable.forget(),NS_DISPATCH_NORMAL);}else{rv=self->mThread->DispatchAnyThread(WorkerThreadFriendKey(),runnable.forget());}if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}mCondVar.Notify();}returnNS_OK;}template<classDerived>voidWorkerPrivateParent<Derived>::EnableDebugger(){AssertIsOnParentThread();WorkerPrivate*self=ParentAsWorkerPrivate();if(NS_FAILED(RegisterWorkerDebugger(self))){NS_WARNING("Failed to register worker debugger!");return;}}template<classDerived>voidWorkerPrivateParent<Derived>::DisableDebugger(){AssertIsOnParentThread();WorkerPrivate*self=ParentAsWorkerPrivate();if(NS_FAILED(UnregisterWorkerDebugger(self))){NS_WARNING("Failed to unregister worker debugger!");}}template<classDerived>nsresultWorkerPrivateParent<Derived>::DispatchControlRunnable(already_AddRefed<WorkerControlRunnable>aWorkerControlRunnable){// May be called on any thread!RefPtr<WorkerControlRunnable>runnable(aWorkerControlRunnable);MOZ_ASSERT(runnable);WorkerPrivate*self=ParentAsWorkerPrivate();{MutexAutoLocklock(mMutex);if(self->mStatus==Dead){returnNS_ERROR_UNEXPECTED;}// Transfer ownership to the control queue.self->mControlQueue.Push(runnable.forget().take());if(JSContext*cx=self->mJSContext){MOZ_ASSERT(self->mThread);JS_RequestInterruptCallback(cx);}mCondVar.Notify();}returnNS_OK;}template<classDerived>nsresultWorkerPrivateParent<Derived>::DispatchDebuggerRunnable(already_AddRefed<WorkerRunnable>aDebuggerRunnable){// May be called on any thread!RefPtr<WorkerRunnable>runnable(aDebuggerRunnable);MOZ_ASSERT(runnable);WorkerPrivate*self=ParentAsWorkerPrivate();{MutexAutoLocklock(mMutex);if(self->mStatus==Dead){NS_WARNING("A debugger runnable was posted to a worker that is already ""shutting down!");returnNS_ERROR_UNEXPECTED;}// Transfer ownership to the debugger queue.self->mDebuggerQueue.Push(runnable.forget().take());mCondVar.Notify();}returnNS_OK;}template<classDerived>already_AddRefed<WorkerRunnable>WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable>aRunnable){// May be called on any thread!nsCOMPtr<nsIRunnable>runnable(aRunnable);MOZ_ASSERT(runnable);RefPtr<WorkerRunnable>workerRunnable=WorkerRunnable::FromRunnable(runnable);if(workerRunnable){returnworkerRunnable.forget();}nsCOMPtr<nsICancelableRunnable>cancelable=do_QueryInterface(runnable);if(!cancelable){MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");}workerRunnable=newExternalRunnableWrapper(ParentAsWorkerPrivate(),runnable);returnworkerRunnable.forget();}template<classDerived>already_AddRefed<nsISerialEventTarget>WorkerPrivateParent<Derived>::GetEventTarget(){WorkerPrivate*self=ParentAsWorkerPrivate();nsCOMPtr<nsISerialEventTarget>target;boolneedAutoDisable=false;{MutexAutoLocklock(mMutex);if(!mEventTarget){mEventTarget=newEventTarget(self);// If the worker is already shutting down then we want to// immediately disable the event target. This will cause// the Dispatch() method to fail, but the event target// will still exist.if(self->mStatus>Running){needAutoDisable=true;}}target=mEventTarget;}// Make sure to call Disable() outside of the mutex since it// also internally locks a mutex.if(needAutoDisable){mEventTarget->Disable();}MOZ_DIAGNOSTIC_ASSERT(target);returntarget.forget();}template<classDerived>boolWorkerPrivateParent<Derived>::Start(){// May be called on any thread!{MutexAutoLocklock(mMutex);NS_ASSERTION(mParentStatus!=Running,"How can this be?!");if(mParentStatus==Pending){mParentStatus=Running;returntrue;}}returnfalse;}// aCx is null when called from the finalizertemplate<classDerived>boolWorkerPrivateParent<Derived>::NotifyPrivate(StatusaStatus){AssertIsOnParentThread();boolpending;{MutexAutoLocklock(mMutex);if(mParentStatus>=aStatus){returntrue;}pending=mParentStatus==Pending;mParentStatus=aStatus;}if(IsSharedWorker()){RuntimeService*runtime=RuntimeService::GetService();MOZ_ASSERT(runtime);runtime->ForgetSharedWorker(ParentAsWorkerPrivate());}if(pending){WorkerPrivate*self=ParentAsWorkerPrivate();#ifdef DEBUG{// Fake a thread here just so that our assertions don't go off for no// reason.nsIThread*currentThread=NS_GetCurrentThread();MOZ_ASSERT(currentThread);MOZ_ASSERT(!self->mPRThread);self->mPRThread=PRThreadFromThread(currentThread);MOZ_ASSERT(self->mPRThread);}#endif// Worker never got a chance to run, go ahead and delete it.self->ScheduleDeletion(WorkerPrivate::WorkerNeverRan);returntrue;}NS_ASSERTION(aStatus!=Terminating||mQueuedRunnables.IsEmpty(),"Shouldn't have anything queued!");// Anything queued will be discarded.mQueuedRunnables.Clear();RefPtr<NotifyRunnable>runnable=newNotifyRunnable(ParentAsWorkerPrivate(),aStatus);returnrunnable->Dispatch();}template<classDerived>boolWorkerPrivateParent<Derived>::Freeze(nsPIDOMWindowInner*aWindow){AssertIsOnParentThread();// Shared workers are only frozen if all of their owning documents are// frozen. It can happen that mSharedWorkers is empty but this thread has// not been unregistered yet.if((IsSharedWorker()||IsServiceWorker())&&!mSharedWorkers.IsEmpty()){AssertIsOnMainThread();boolallFrozen=true;for(uint32_ti=0;i<mSharedWorkers.Length();++i){if(aWindow&&mSharedWorkers[i]->GetOwner()==aWindow){// Calling Freeze() may change the refcount, ensure that the worker// outlives this call.RefPtr<SharedWorker>kungFuDeathGrip=mSharedWorkers[i];kungFuDeathGrip->Freeze();}else{MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner()&&aWindow,!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),aWindow));if(!mSharedWorkers[i]->IsFrozen()){allFrozen=false;}}}if(!allFrozen||mParentFrozen){returntrue;}}mParentFrozen=true;{MutexAutoLocklock(mMutex);if(mParentStatus>=Terminating){returntrue;}}DisableDebugger();RefPtr<FreezeRunnable>runnable=newFreezeRunnable(ParentAsWorkerPrivate());if(!runnable->Dispatch()){returnfalse;}returntrue;}template<classDerived>boolWorkerPrivateParent<Derived>::Thaw(nsPIDOMWindowInner*aWindow){AssertIsOnParentThread();MOZ_ASSERT(mParentFrozen);// Shared workers are resumed if any of their owning documents are thawed.// It can happen that mSharedWorkers is empty but this thread has not been// unregistered yet.if((IsSharedWorker()||IsServiceWorker())&&!mSharedWorkers.IsEmpty()){AssertIsOnMainThread();boolanyRunning=false;for(uint32_ti=0;i<mSharedWorkers.Length();++i){if(aWindow&&mSharedWorkers[i]->GetOwner()==aWindow){// Calling Thaw() may change the refcount, ensure that the worker// outlives this call.RefPtr<SharedWorker>kungFuDeathGrip=mSharedWorkers[i];kungFuDeathGrip->Thaw();anyRunning=true;}else{MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner()&&aWindow,!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),aWindow));if(!mSharedWorkers[i]->IsFrozen()){anyRunning=true;}}}if(!anyRunning||!mParentFrozen){returntrue;}}MOZ_ASSERT(mParentFrozen);mParentFrozen=false;{MutexAutoLocklock(mMutex);if(mParentStatus>=Terminating){returntrue;}}EnableDebugger();// Execute queued runnables before waking up the worker, otherwise the worker// could post new messages before we run those that have been queued.if(!IsParentWindowPaused()&&!mQueuedRunnables.IsEmpty()){MOZ_ASSERT(IsDedicatedWorker());nsTArray<nsCOMPtr<nsIRunnable>>runnables;mQueuedRunnables.SwapElements(runnables);for(uint32_tindex=0;index<runnables.Length();index++){runnables[index]->Run();}}RefPtr<ThawRunnable>runnable=newThawRunnable(ParentAsWorkerPrivate());if(!runnable->Dispatch()){returnfalse;}returntrue;}template<classDerived>voidWorkerPrivateParent<Derived>::ParentWindowPaused(){AssertIsOnMainThread();MOZ_ASSERT_IF(IsDedicatedWorker(),mParentWindowPausedDepth==0);mParentWindowPausedDepth+=1;}template<classDerived>voidWorkerPrivateParent<Derived>::ParentWindowResumed(){AssertIsOnMainThread();MOZ_ASSERT(mParentWindowPausedDepth>0);MOZ_ASSERT_IF(IsDedicatedWorker(),mParentWindowPausedDepth==1);mParentWindowPausedDepth-=1;if(mParentWindowPausedDepth>0){return;}{MutexAutoLocklock(mMutex);if(mParentStatus>=Terminating){return;}}// Execute queued runnables before waking up, otherwise the worker could post// new messages before we run those that have been queued.if(!IsFrozen()&&!mQueuedRunnables.IsEmpty()){MOZ_ASSERT(IsDedicatedWorker());nsTArray<nsCOMPtr<nsIRunnable>>runnables;mQueuedRunnables.SwapElements(runnables);for(uint32_tindex=0;index<runnables.Length();index++){runnables[index]->Run();}}}template<classDerived>boolWorkerPrivateParent<Derived>::Close(){mMutex.AssertCurrentThreadOwns();if(mParentStatus<Closing){mParentStatus=Closing;}returntrue;}template<classDerived>boolWorkerPrivateParent<Derived>::ModifyBusyCount(boolaIncrease){AssertIsOnParentThread();NS_ASSERTION(aIncrease||mBusyCount,"Mismatched busy count mods!");if(aIncrease){mBusyCount++;returntrue;}if(--mBusyCount==0){boolshouldCancel;{MutexAutoLocklock(mMutex);shouldCancel=mParentStatus==Terminating;}if(shouldCancel&&!Cancel()){returnfalse;}}returntrue;}template<classDerived>boolWorkerPrivateParent<Derived>::ProxyReleaseMainThreadObjects(){AssertIsOnParentThread();MOZ_ASSERT(!mMainThreadObjectsForgotten);nsCOMPtr<nsILoadGroup>loadGroupToCancel;// If we're not overriden, then do nothing here. Let the load group get// handled in ForgetMainThreadObjects().if(mLoadInfo.mInterfaceRequestor){mLoadInfo.mLoadGroup.swap(loadGroupToCancel);}boolresult=mLoadInfo.ProxyReleaseMainThreadObjects(ParentAsWorkerPrivate(),loadGroupToCancel);mMainThreadObjectsForgotten=true;returnresult;}template<classDerived>voidWorkerPrivateParent<Derived>::PostMessageInternal(JSContext*aCx,JS::Handle<JS::Value>aMessage,constSequence<JSObject*>&aTransferable,ErrorResult&aRv){AssertIsOnParentThread();{MutexAutoLocklock(mMutex);if(mParentStatus>Running){return;}}JS::Rooted<JS::Value>transferable(aCx,JS::UndefinedValue());aRv=nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,aTransferable,&transferable);if(NS_WARN_IF(aRv.Failed())){return;}RefPtr<MessageEventRunnable>runnable=newMessageEventRunnable(ParentAsWorkerPrivate(),WorkerRunnable::WorkerThreadModifyBusyCount);UniquePtr<AbstractTimelineMarker>start;UniquePtr<AbstractTimelineMarker>end;RefPtr<TimelineConsumers>timelines=TimelineConsumers::Get();boolisTimelineRecording=timelines&&!timelines->IsEmpty();if(isTimelineRecording){start=MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()?ProfileTimelineWorkerOperationType::SerializeDataOnMainThread:ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,MarkerTracingType::START);}runnable->Write(aCx,aMessage,transferable,JS::CloneDataPolicy(),aRv);if(isTimelineRecording){end=MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()?ProfileTimelineWorkerOperationType::SerializeDataOnMainThread:ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,MarkerTracingType::END);timelines->AddMarkerForAllObservedDocShells(start);timelines->AddMarkerForAllObservedDocShells(end);}if(NS_WARN_IF(aRv.Failed())){return;}if(!runnable->Dispatch()){aRv.Throw(NS_ERROR_FAILURE);}}template<classDerived>voidWorkerPrivateParent<Derived>::PostMessage(JSContext*aCx,JS::Handle<JS::Value>aMessage,constSequence<JSObject*>&aTransferable,ErrorResult&aRv){PostMessageInternal(aCx,aMessage,aTransferable,aRv);}template<classDerived>voidWorkerPrivateParent<Derived>::UpdateContextOptions(constJS::ContextOptions&aContextOptions){AssertIsOnParentThread();{MutexAutoLocklock(mMutex);mJSSettings.contextOptions=aContextOptions;}RefPtr<UpdateContextOptionsRunnable>runnable=newUpdateContextOptionsRunnable(ParentAsWorkerPrivate(),aContextOptions);if(!runnable->Dispatch()){NS_WARNING("Failed to update worker context options!");}}template<classDerived>voidWorkerPrivateParent<Derived>::UpdatePreference(WorkerPreferenceaPref,boolaValue){AssertIsOnParentThread();MOZ_ASSERT(aPref>=0&&aPref<WORKERPREF_COUNT);RefPtr<UpdatePreferenceRunnable>runnable=newUpdatePreferenceRunnable(ParentAsWorkerPrivate(),aPref,aValue);if(!runnable->Dispatch()){NS_WARNING("Failed to update worker preferences!");}}template<classDerived>voidWorkerPrivateParent<Derived>::UpdateLanguages(constnsTArray<nsString>&aLanguages){AssertIsOnParentThread();RefPtr<UpdateLanguagesRunnable>runnable=newUpdateLanguagesRunnable(ParentAsWorkerPrivate(),aLanguages);if(!runnable->Dispatch()){NS_WARNING("Failed to update worker languages!");}}template<classDerived>voidWorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSGCParamKeyaKey,uint32_taValue){AssertIsOnParentThread();boolfound=false;{MutexAutoLocklock(mMutex);found=mJSSettings.ApplyGCSetting(aKey,aValue);}if(found){RefPtr<UpdateJSWorkerMemoryParameterRunnable>runnable=newUpdateJSWorkerMemoryParameterRunnable(ParentAsWorkerPrivate(),aKey,aValue);if(!runnable->Dispatch()){NS_WARNING("Failed to update memory parameter!");}}}#ifdef JS_GC_ZEALtemplate<classDerived>voidWorkerPrivateParent<Derived>::UpdateGCZeal(uint8_taGCZeal,uint32_taFrequency){AssertIsOnParentThread();{MutexAutoLocklock(mMutex);mJSSettings.gcZeal=aGCZeal;mJSSettings.gcZealFrequency=aFrequency;}RefPtr<UpdateGCZealRunnable>runnable=newUpdateGCZealRunnable(ParentAsWorkerPrivate(),aGCZeal,aFrequency);if(!runnable->Dispatch()){NS_WARNING("Failed to update worker gczeal!");}}#endiftemplate<classDerived>voidWorkerPrivateParent<Derived>::GarbageCollect(boolaShrinking){AssertIsOnParentThread();RefPtr<GarbageCollectRunnable>runnable=newGarbageCollectRunnable(ParentAsWorkerPrivate(),aShrinking,/* collectChildren = */true);if(!runnable->Dispatch()){NS_WARNING("Failed to GC worker!");}}template<classDerived>voidWorkerPrivateParent<Derived>::CycleCollect(boolaDummy){AssertIsOnParentThread();RefPtr<CycleCollectRunnable>runnable=newCycleCollectRunnable(ParentAsWorkerPrivate(),/* collectChildren = */true);if(!runnable->Dispatch()){NS_WARNING("Failed to CC worker!");}}template<classDerived>voidWorkerPrivateParent<Derived>::OfflineStatusChangeEvent(boolaIsOffline){AssertIsOnParentThread();RefPtr<OfflineStatusChangeRunnable>runnable=newOfflineStatusChangeRunnable(ParentAsWorkerPrivate(),aIsOffline);if(!runnable->Dispatch()){NS_WARNING("Failed to dispatch offline status change event!");}}voidWorkerPrivate::OfflineStatusChangeEventInternal(boolaIsOffline){AssertIsOnWorkerThread();// The worker is already in this state. No need to dispatch an event.if(mOnLine==!aIsOffline){return;}for(uint32_tindex=0;index<mChildWorkers.Length();++index){mChildWorkers[index]->OfflineStatusChangeEvent(aIsOffline);}mOnLine=!aIsOffline;WorkerGlobalScope*globalScope=GlobalScope();RefPtr<WorkerNavigator>nav=globalScope->GetExistingNavigator();if(nav){nav->SetOnLine(mOnLine);}nsStringeventType;if(aIsOffline){eventType.AssignLiteral("offline");}else{eventType.AssignLiteral("online");}RefPtr<Event>event=NS_NewDOMEvent(globalScope,nullptr,nullptr);event->InitEvent(eventType,false,false);event->SetTrusted(true);globalScope->DispatchDOMEvent(nullptr,event,nullptr,nullptr);}template<classDerived>voidWorkerPrivateParent<Derived>::MemoryPressure(boolaDummy){AssertIsOnParentThread();RefPtr<MemoryPressureRunnable>runnable=newMemoryPressureRunnable(ParentAsWorkerPrivate());Unused<<NS_WARN_IF(!runnable->Dispatch());}template<classDerived>boolWorkerPrivateParent<Derived>::RegisterSharedWorker(SharedWorker*aSharedWorker,MessagePort*aPort){AssertIsOnMainThread();MOZ_ASSERT(aSharedWorker);MOZ_ASSERT(IsSharedWorker());MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));if(IsSharedWorker()){RefPtr<MessagePortRunnable>runnable=newMessagePortRunnable(ParentAsWorkerPrivate(),aPort);if(!runnable->Dispatch()){returnfalse;}}mSharedWorkers.AppendElement(aSharedWorker);// If there were other SharedWorker objects attached to this worker then they// may all have been frozen and this worker would need to be thawed.if(mSharedWorkers.Length()>1&&IsFrozen()&&!Thaw(nullptr)){returnfalse;}returntrue;}template<classDerived>voidWorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(JSContext*aCx,constWorkerErrorReport*aReport,boolaIsErrorEvent){AssertIsOnMainThread();if(aIsErrorEvent&&JSREPORT_IS_WARNING(aReport->mFlags)){// Don't fire any events anywhere. Just log to console.// XXXbz should we log to all the consoles of all the relevant windows?MOZ_ASSERT(aReport);LogErrorToConsole(*aReport,0);return;}AutoTArray<RefPtr<SharedWorker>,10>sharedWorkers;GetAllSharedWorkers(sharedWorkers);if(sharedWorkers.IsEmpty()){return;}AutoTArray<WindowAction,10>windowActions;nsresultrv;// First fire the error event at all SharedWorker objects. This may include// multiple objects in a single window as well as objects in different// windows.for(size_tindex=0;index<sharedWorkers.Length();index++){RefPtr<SharedWorker>&sharedWorker=sharedWorkers[index];// May be null.nsPIDOMWindowInner*window=sharedWorker->GetOwner();RefPtr<Event>event;if(aIsErrorEvent){RootedDictionary<ErrorEventInit>errorInit(aCx);errorInit.mBubbles=false;errorInit.mCancelable=true;errorInit.mMessage=aReport->mMessage;errorInit.mFilename=aReport->mFilename;errorInit.mLineno=aReport->mLineNumber;errorInit.mColno=aReport->mColumnNumber;event=ErrorEvent::Constructor(sharedWorker,NS_LITERAL_STRING("error"),errorInit);}else{event=Event::Constructor(sharedWorker,NS_LITERAL_STRING("error"),EventInit());}if(!event){ThrowAndReport(window,NS_ERROR_UNEXPECTED);continue;}event->SetTrusted(true);booldefaultActionEnabled;nsresultrv=sharedWorker->DispatchEvent(event,&defaultActionEnabled);if(NS_FAILED(rv)){ThrowAndReport(window,rv);continue;}if(!aIsErrorEvent){continue;}if(defaultActionEnabled){// Add the owning window to our list so that we will fire an error event// at it later.if(!windowActions.Contains(window)){windowActions.AppendElement(WindowAction(window));}}else{size_tactionsIndex=windowActions.LastIndexOf(WindowAction(window));if(actionsIndex!=windowActions.NoIndex){// Any listener that calls preventDefault() will prevent the window from// receiving the error event.windowActions[actionsIndex].mDefaultAction=false;}}}// If there are no windows to consider further then we're done.if(windowActions.IsEmpty()){return;}boolshouldLogErrorToConsole=true;// Now fire error events at all the windows remaining.for(uint32_tindex=0;index<windowActions.Length();index++){WindowAction&windowAction=windowActions[index];// If there is no window or the script already called preventDefault then// skip this window.if(!windowAction.mWindow||!windowAction.mDefaultAction){continue;}nsCOMPtr<nsIScriptGlobalObject>sgo=do_QueryInterface(windowAction.mWindow);MOZ_ASSERT(sgo);MOZ_ASSERT(NS_IsMainThread());RootedDictionary<ErrorEventInit>init(aCx);init.mLineno=aReport->mLineNumber;init.mFilename=aReport->mFilename;init.mMessage=aReport->mMessage;init.mCancelable=true;init.mBubbles=true;nsEventStatusstatus=nsEventStatus_eIgnore;rv=sgo->HandleScriptError(init,&status);if(NS_FAILED(rv)){ThrowAndReport(windowAction.mWindow,rv);continue;}if(status==nsEventStatus_eConsumeNoDefault){shouldLogErrorToConsole=false;}}// Finally log a warning in the console if no window tried to prevent it.if(shouldLogErrorToConsole){MOZ_ASSERT(aReport);LogErrorToConsole(*aReport,0);}}template<classDerived>voidWorkerPrivateParent<Derived>::GetAllSharedWorkers(nsTArray<RefPtr<SharedWorker>>&aSharedWorkers){AssertIsOnMainThread();MOZ_ASSERT(IsSharedWorker()||IsServiceWorker());if(!aSharedWorkers.IsEmpty()){aSharedWorkers.Clear();}for(uint32_ti=0;i<mSharedWorkers.Length();++i){aSharedWorkers.AppendElement(mSharedWorkers[i]);}}template<classDerived>voidWorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(nsPIDOMWindowInner*aWindow){AssertIsOnMainThread();MOZ_ASSERT(IsSharedWorker()||IsServiceWorker());MOZ_ASSERT(aWindow);boolsomeRemoved=false;for(uint32_ti=0;i<mSharedWorkers.Length();){if(mSharedWorkers[i]->GetOwner()==aWindow){mSharedWorkers[i]->Close();mSharedWorkers.RemoveElementAt(i);someRemoved=true;}else{MOZ_ASSERT(!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),aWindow));++i;}}if(!someRemoved){return;}// If there are still SharedWorker objects attached to this worker then they// may all be frozen and this worker would need to be frozen. Otherwise,// if that was the last SharedWorker then it's time to cancel this worker.if(!mSharedWorkers.IsEmpty()){Freeze(nullptr);}else{Cancel();}}template<classDerived>voidWorkerPrivateParent<Derived>::CloseAllSharedWorkers(){AssertIsOnMainThread();MOZ_ASSERT(IsSharedWorker()||IsServiceWorker());for(uint32_ti=0;i<mSharedWorkers.Length();++i){mSharedWorkers[i]->Close();}mSharedWorkers.Clear();Cancel();}template<classDerived>voidWorkerPrivateParent<Derived>::WorkerScriptLoaded(){AssertIsOnMainThread();if(IsSharedWorker()||IsServiceWorker()){// No longer need to hold references to the window or document we came from.mLoadInfo.mWindow=nullptr;mLoadInfo.mScriptContext=nullptr;}}template<classDerived>voidWorkerPrivateParent<Derived>::SetBaseURI(nsIURI*aBaseURI){AssertIsOnMainThread();if(!mLoadInfo.mBaseURI){NS_ASSERTION(GetParent(),"Shouldn't happen without a parent!");mLoadInfo.mResolvedScriptURI=aBaseURI;}mLoadInfo.mBaseURI=aBaseURI;if(NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))){mLocationInfo.mHref.Truncate();}mLocationInfo.mHostname.Truncate();nsContentUtils::GetHostOrIPv6WithBrackets(aBaseURI,mLocationInfo.mHostname);nsCOMPtr<nsIURL>url(do_QueryInterface(aBaseURI));if(!url||NS_FAILED(url->GetFilePath(mLocationInfo.mPathname))){mLocationInfo.mPathname.Truncate();}nsCStringtemp;if(url&&NS_SUCCEEDED(url->GetQuery(temp))&&!temp.IsEmpty()){mLocationInfo.mSearch.Assign('?');mLocationInfo.mSearch.Append(temp);}if(NS_SUCCEEDED(aBaseURI->GetRef(temp))&&!temp.IsEmpty()){if(mLocationInfo.mHash.IsEmpty()){mLocationInfo.mHash.Assign('#');mLocationInfo.mHash.Append(temp);}}if(NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))){mLocationInfo.mProtocol.Append(':');}else{mLocationInfo.mProtocol.Truncate();}int32_tport;if(NS_SUCCEEDED(aBaseURI->GetPort(&port))&&port!=-1){mLocationInfo.mPort.AppendInt(port);nsAutoCStringhost(mLocationInfo.mHostname);host.Append(':');host.Append(mLocationInfo.mPort);mLocationInfo.mHost.Assign(host);}else{mLocationInfo.mHost.Assign(mLocationInfo.mHostname);}nsContentUtils::GetUTFOrigin(aBaseURI,mLocationInfo.mOrigin);}template<classDerived>nsresultWorkerPrivateParent<Derived>::SetPrincipalOnMainThread(nsIPrincipal*aPrincipal,nsILoadGroup*aLoadGroup){returnmLoadInfo.SetPrincipalOnMainThread(aPrincipal,aLoadGroup);}template<classDerived>nsresultWorkerPrivateParent<Derived>::SetPrincipalFromChannel(nsIChannel*aChannel){returnmLoadInfo.SetPrincipalFromChannel(aChannel);}#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLEDtemplate<classDerived>boolWorkerPrivateParent<Derived>::FinalChannelPrincipalIsValid(nsIChannel*aChannel){returnmLoadInfo.FinalChannelPrincipalIsValid(aChannel);}template<classDerived>boolWorkerPrivateParent<Derived>::PrincipalURIMatchesScriptURL(){returnmLoadInfo.PrincipalURIMatchesScriptURL();}#endiftemplate<classDerived>voidWorkerPrivateParent<Derived>::UpdateOverridenLoadGroup(nsILoadGroup*aBaseLoadGroup){AssertIsOnMainThread();// The load group should have been overriden at init time.mLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aBaseLoadGroup);}template<classDerived>voidWorkerPrivateParent<Derived>::FlushReportsToSharedWorkers(nsIConsoleReportCollector*aReporter){AssertIsOnMainThread();AutoTArray<RefPtr<SharedWorker>,10>sharedWorkers;AutoTArray<WindowAction,10>windowActions;GetAllSharedWorkers(sharedWorkers);// First find out all the shared workers' window.for(size_tindex=0;index<sharedWorkers.Length();index++){RefPtr<SharedWorker>&sharedWorker=sharedWorkers[index];// May be null.nsPIDOMWindowInner*window=sharedWorker->GetOwner();// Add the owning window to our list so that we will flush the reports later.if(window&&!windowActions.Contains(window)){windowActions.AppendElement(WindowAction(window));}}boolreportErrorToBrowserConsole=true;// Flush the reports.for(uint32_tindex=0;index<windowActions.Length();index++){WindowAction&windowAction=windowActions[index];aReporter->FlushReportsToConsole(windowAction.mWindow->WindowID(),nsIConsoleReportCollector::ReportAction::Save);reportErrorToBrowserConsole=false;}// Finally report to browser console if there is no any window or shared// worker.if(reportErrorToBrowserConsole){aReporter->FlushReportsToConsole(0);return;}aReporter->ClearConsoleReports();}template<classDerived>NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>,DOMEventTargetHelper)template<classDerived>NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>,DOMEventTargetHelper)template<classDerived>NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>)NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)template<classDerived>NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,DOMEventTargetHelper)tmp->AssertIsOnParentThread();// The WorkerPrivate::mSelfRef has a reference to itself, which is really// held by the worker thread. We traverse this reference if and only if our// busy count is zero and we have not released the main thread reference.// We do not unlink it. This allows the CC to break cycles involving the// WorkerPrivate and begin shutting it down (which does happen in unlink) but// ensures that the WorkerPrivate won't be deleted before we're done shutting// down the thread.if(!tmp->mBusyCount&&!tmp->mMainThreadObjectsForgotten){NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelfRef)}// The various strong references in LoadInfo are managed manually and cannot// be cycle collected.NS_IMPL_CYCLE_COLLECTION_TRAVERSE_ENDtemplate<classDerived>NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>,DOMEventTargetHelper)tmp->Terminate();NS_IMPL_CYCLE_COLLECTION_UNLINK_ENDtemplate<classDerived>NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,DOMEventTargetHelper)NS_IMPL_CYCLE_COLLECTION_TRACE_END#ifdef DEBUGtemplate<classDerived>voidWorkerPrivateParent<Derived>::AssertIsOnParentThread()const{if(GetParent()){GetParent()->AssertIsOnWorkerThread();}else{AssertIsOnMainThread();}}template<classDerived>voidWorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect()const{AssertIsOnParentThread();// Only care about top level workers from windows.if(mParent||!mLoadInfo.mWindow){return;}AssertIsOnMainThread();nsPIDOMWindowOuter*outer=mLoadInfo.mWindow->GetOuterWindow();NS_ASSERTION(outer&&outer->GetCurrentInnerWindow()==mLoadInfo.mWindow,"Inner window no longer correct!");}#endif#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLEDtemplate<classDerived>boolWorkerPrivateParent<Derived>::PrincipalIsValid()const{returnmLoadInfo.PrincipalIsValid();}#endifclassPostDebuggerMessageRunnablefinal:publicRunnable{WorkerDebugger*mDebugger;nsStringmMessage;public:PostDebuggerMessageRunnable(WorkerDebugger*aDebugger,constnsAString&aMessage):mozilla::Runnable("PostDebuggerMessageRunnable"),mDebugger(aDebugger),mMessage(aMessage){}private:~PostDebuggerMessageRunnable(){}NS_IMETHODRun()override{mDebugger->PostMessageToDebuggerOnMainThread(mMessage);returnNS_OK;}};classReportDebuggerErrorRunnablefinal:publicRunnable{WorkerDebugger*mDebugger;nsStringmFilename;uint32_tmLineno;nsStringmMessage;public:ReportDebuggerErrorRunnable(WorkerDebugger*aDebugger,constnsAString&aFilename,uint32_taLineno,constnsAString&aMessage):mozilla::Runnable("ReportDebuggerErrorRunnable"),mDebugger(aDebugger),mFilename(aFilename),mLineno(aLineno),mMessage(aMessage){}private:~ReportDebuggerErrorRunnable(){}NS_IMETHODRun()override{mDebugger->ReportErrorToDebuggerOnMainThread(mFilename,mLineno,mMessage);returnNS_OK;}};WorkerDebugger::WorkerDebugger(WorkerPrivate*aWorkerPrivate):mWorkerPrivate(aWorkerPrivate),mIsInitialized(false){AssertIsOnMainThread();}WorkerDebugger::~WorkerDebugger(){MOZ_ASSERT(!mWorkerPrivate);if(!NS_IsMainThread()){for(size_tindex=0;index<mListeners.Length();++index){NS_ReleaseOnMainThread("WorkerDebugger::mListeners",mListeners[index].forget());}}}NS_IMPL_ISUPPORTS(WorkerDebugger,nsIWorkerDebugger)NS_IMETHODIMPWorkerDebugger::GetIsClosed(bool*aResult){AssertIsOnMainThread();*aResult=!mWorkerPrivate;returnNS_OK;}NS_IMETHODIMPWorkerDebugger::GetIsChrome(bool*aResult){AssertIsOnMainThread();if(!mWorkerPrivate){returnNS_ERROR_UNEXPECTED;}*aResult=mWorkerPrivate->IsChromeWorker();returnNS_OK;}NS_IMETHODIMPWorkerDebugger::GetIsInitialized(bool*aResult){AssertIsOnMainThread();if(!mWorkerPrivate){returnNS_ERROR_UNEXPECTED;}*aResult=mIsInitialized;returnNS_OK;}NS_IMETHODIMPWorkerDebugger::GetParent(nsIWorkerDebugger**aResult){AssertIsOnMainThread();if(!mWorkerPrivate){returnNS_ERROR_UNEXPECTED;}WorkerPrivate*parent=mWorkerPrivate->GetParent();if(!parent){*aResult=nullptr;returnNS_OK;}MOZ_ASSERT(mWorkerPrivate->IsDedicatedWorker());nsCOMPtr<nsIWorkerDebugger>debugger=parent->Debugger();debugger.forget(aResult);returnNS_OK;}NS_IMETHODIMPWorkerDebugger::GetType(uint32_t*aResult){AssertIsOnMainThread();if(!mWorkerPrivate){returnNS_ERROR_UNEXPECTED;}*aResult=mWorkerPrivate->Type();returnNS_OK;}NS_IMETHODIMPWorkerDebugger::GetUrl(nsAString&aResult){AssertIsOnMainThread();if(!mWorkerPrivate){returnNS_ERROR_UNEXPECTED;}aResult=mWorkerPrivate->ScriptURL();returnNS_OK;}NS_IMETHODIMPWorkerDebugger::GetWindow(mozIDOMWindow**aResult){AssertIsOnMainThread();if(!mWorkerPrivate){returnNS_ERROR_UNEXPECTED;}if(mWorkerPrivate->GetParent()||!mWorkerPrivate->IsDedicatedWorker()){*aResult=nullptr;returnNS_OK;}nsCOMPtr<nsPIDOMWindowInner>window=mWorkerPrivate->GetWindow();window.forget(aResult);returnNS_OK;}NS_IMETHODIMPWorkerDebugger::GetPrincipal(nsIPrincipal**aResult){AssertIsOnMainThread();MOZ_ASSERT(aResult);if(!mWorkerPrivate){returnNS_ERROR_UNEXPECTED;}nsCOMPtr<nsIPrincipal>prin=mWorkerPrivate->GetPrincipal();prin.forget(aResult);returnNS_OK;}NS_IMETHODIMPWorkerDebugger::GetServiceWorkerID(uint32_t*aResult){AssertIsOnMainThread();MOZ_ASSERT(aResult);if(!mWorkerPrivate||!mWorkerPrivate->IsServiceWorker()){returnNS_ERROR_UNEXPECTED;}*aResult=mWorkerPrivate->ServiceWorkerID();returnNS_OK;}NS_IMETHODIMPWorkerDebugger::Initialize(constnsAString&aURL){AssertIsOnMainThread();if(!mWorkerPrivate){returnNS_ERROR_UNEXPECTED;}if(!mIsInitialized){RefPtr<CompileDebuggerScriptRunnable>runnable=newCompileDebuggerScriptRunnable(mWorkerPrivate,aURL);if(!runnable->Dispatch()){returnNS_ERROR_FAILURE;}mIsInitialized=true;}returnNS_OK;}NS_IMETHODIMPWorkerDebugger::PostMessageMoz(constnsAString&aMessage){AssertIsOnMainThread();if(!mWorkerPrivate||!mIsInitialized){returnNS_ERROR_UNEXPECTED;}RefPtr<DebuggerMessageEventRunnable>runnable=newDebuggerMessageEventRunnable(mWorkerPrivate,aMessage);if(!runnable->Dispatch()){returnNS_ERROR_FAILURE;}returnNS_OK;}NS_IMETHODIMPWorkerDebugger::AddListener(nsIWorkerDebuggerListener*aListener){AssertIsOnMainThread();if(mListeners.Contains(aListener)){returnNS_ERROR_INVALID_ARG;}mListeners.AppendElement(aListener);returnNS_OK;}NS_IMETHODIMPWorkerDebugger::RemoveListener(nsIWorkerDebuggerListener*aListener){AssertIsOnMainThread();if(!mListeners.Contains(aListener)){returnNS_ERROR_INVALID_ARG;}mListeners.RemoveElement(aListener);returnNS_OK;}voidWorkerDebugger::Close(){MOZ_ASSERT(mWorkerPrivate);mWorkerPrivate=nullptr;nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>>listeners(mListeners);for(size_tindex=0;index<listeners.Length();++index){listeners[index]->OnClose();}}voidWorkerDebugger::PostMessageToDebugger(constnsAString&aMessage){mWorkerPrivate->AssertIsOnWorkerThread();RefPtr<PostDebuggerMessageRunnable>runnable=newPostDebuggerMessageRunnable(this,aMessage);if(NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))){NS_WARNING("Failed to post message to debugger on main thread!");}}voidWorkerDebugger::PostMessageToDebuggerOnMainThread(constnsAString&aMessage){AssertIsOnMainThread();nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>>listeners(mListeners);for(size_tindex=0;index<listeners.Length();++index){listeners[index]->OnMessage(aMessage);}}voidWorkerDebugger::ReportErrorToDebugger(constnsAString&aFilename,uint32_taLineno,constnsAString&aMessage){mWorkerPrivate->AssertIsOnWorkerThread();RefPtr<ReportDebuggerErrorRunnable>runnable=newReportDebuggerErrorRunnable(this,aFilename,aLineno,aMessage);if(NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))){NS_WARNING("Failed to report error to debugger on main thread!");}}voidWorkerDebugger::ReportErrorToDebuggerOnMainThread(constnsAString&aFilename,uint32_taLineno,constnsAString&aMessage){AssertIsOnMainThread();nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>>listeners(mListeners);for(size_tindex=0;index<listeners.Length();++index){listeners[index]->OnError(aFilename,aLineno,aMessage);}WorkerErrorReportreport;report.mMessage=aMessage;report.mFilename=aFilename;LogErrorToConsole(report,0);}WorkerPrivate::WorkerPrivate(WorkerPrivate*aParent,constnsAString&aScriptURL,boolaIsChromeWorker,WorkerTypeaWorkerType,constnsAString&aWorkerName,constnsACString&aServiceWorkerScope,WorkerLoadInfo&aLoadInfo):WorkerPrivateParent<WorkerPrivate>(aParent,aScriptURL,aIsChromeWorker,aWorkerType,aWorkerName,aServiceWorkerScope,aLoadInfo),mDebuggerRegistered(false),mDebugger(nullptr),mJSContext(nullptr),mPRThread(nullptr),mNumHoldersPreventingShutdownStart(0),mDebuggerEventLoopLevel(0),mMainThreadEventTarget(GetMainThreadEventTarget()),mWorkerControlEventTarget(newWorkerControlEventTarget(this)),mErrorHandlerRecursionCount(0),mNextTimeoutId(1),mStatus(Pending),mFrozen(false),mTimerRunning(false),mRunningExpiredTimeouts(false),mPendingEventQueueClearing(false),mCancelAllPendingRunnables(false),mPeriodicGCTimerRunning(false),mIdleGCTimerRunning(false),mWorkerScriptExecutedSuccessfully(false),mFetchHandlerWasAdded(false),mOnLine(false){if(aParent){aParent->AssertIsOnWorkerThread();aParent->GetAllPreferences(mPreferences);mOnLine=aParent->OnLine();}else{AssertIsOnMainThread();RuntimeService::GetDefaultPreferences(mPreferences);mOnLine=!NS_IsOffline();}nsCOMPtr<nsISerialEventTarget>target;// A child worker just inherits the parent workers ThrottledEventQueue// and main thread target for now. This is mainly due to the restriction// that ThrottledEventQueue can only be created on the main thread at the// moment.if(aParent){mMainThreadThrottledEventQueue=aParent->mMainThreadThrottledEventQueue;mMainThreadEventTarget=aParent->mMainThreadEventTarget;return;}MOZ_ASSERT(NS_IsMainThread());target=GetWindow()?GetWindow()->EventTargetFor(TaskCategory::Worker):nullptr;if(!target){target=GetMainThreadSerialEventTarget();MOZ_DIAGNOSTIC_ASSERT(target);}// Throttle events to the main thread using a ThrottledEventQueue specific to// this worker thread. This may return nullptr during shutdown.mMainThreadThrottledEventQueue=ThrottledEventQueue::Create(target);// If we were able to creat the throttled event queue, then use it for// dispatching our main thread runnables. Otherwise use our underlying// base target.if(mMainThreadThrottledEventQueue){mMainThreadEventTarget=mMainThreadThrottledEventQueue;}else{mMainThreadEventTarget=target.forget();}}WorkerPrivate::~WorkerPrivate(){mWorkerControlEventTarget->ForgetWorkerPrivate(this);}// staticalready_AddRefed<WorkerPrivate>WorkerPrivate::Constructor(constGlobalObject&aGlobal,constnsAString&aScriptURL,constWorkerOptions&aOptions,ErrorResult&aRv){returnWorkerPrivate::Constructor(aGlobal,aScriptURL,false,WorkerTypeDedicated,aOptions.mName,nullptr,aRv);}// staticboolWorkerPrivate::WorkerAvailable(JSContext*aCx,JSObject*/* unused */){// If we're already on a worker workers are clearly enabled.if(!NS_IsMainThread()){returntrue;}// If our caller is chrome, workers are always available.if(nsContentUtils::IsSystemCaller(aCx)){returntrue;}// Else check the pref.returnPreferences::GetBool(PREF_WORKERS_ENABLED);}// staticalready_AddRefed<ChromeWorkerPrivate>ChromeWorkerPrivate::Constructor(constGlobalObject&aGlobal,constnsAString&aScriptURL,ErrorResult&aRv){returnWorkerPrivate::Constructor(aGlobal,aScriptURL,true,WorkerTypeDedicated,EmptyString(),nullptr,aRv).downcast<ChromeWorkerPrivate>();}// staticboolChromeWorkerPrivate::WorkerAvailable(JSContext*aCx,JSObject*/* unused */){// Chrome is always allowed to use workers, and content is never// allowed to use ChromeWorker, so all we have to check is the// caller. However, chrome workers apparently might not have a// system principal, so we have to check for them manually.if(NS_IsMainThread()){returnnsContentUtils::IsSystemCaller(aCx);}returnGetWorkerPrivateFromContext(aCx)->IsChromeWorker();}// staticalready_AddRefed<WorkerPrivate>WorkerPrivate::Constructor(constGlobalObject&aGlobal,constnsAString&aScriptURL,boolaIsChromeWorker,WorkerTypeaWorkerType,constnsAString&aWorkerName,WorkerLoadInfo*aLoadInfo,ErrorResult&aRv){JSContext*cx=aGlobal.Context();returnConstructor(cx,aScriptURL,aIsChromeWorker,aWorkerType,aWorkerName,NullCString(),aLoadInfo,aRv);}// staticalready_AddRefed<WorkerPrivate>WorkerPrivate::Constructor(JSContext*aCx,constnsAString&aScriptURL,boolaIsChromeWorker,WorkerTypeaWorkerType,constnsAString&aWorkerName,constnsACString&aServiceWorkerScope,WorkerLoadInfo*aLoadInfo,ErrorResult&aRv){// If this is a sub-worker, we need to keep the parent worker alive until this// one is registered.UniquePtr<SimpleWorkerHolder>holder;WorkerPrivate*parent=NS_IsMainThread()?nullptr:GetCurrentThreadWorkerPrivate();if(parent){parent->AssertIsOnWorkerThread();holder.reset(newSimpleWorkerHolder());if(!holder->HoldWorker(parent,Canceling)){aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);returnnullptr;}}else{AssertIsOnMainThread();}Maybe<WorkerLoadInfo>stackLoadInfo;if(!aLoadInfo){stackLoadInfo.emplace();nsresultrv=GetLoadInfo(aCx,nullptr,parent,aScriptURL,aIsChromeWorker,InheritLoadGroup,aWorkerType,stackLoadInfo.ptr());aRv.MightThrowJSException();if(NS_FAILED(rv)){scriptloader::ReportLoadError(aRv,rv,aScriptURL);returnnullptr;}aLoadInfo=stackLoadInfo.ptr();}// NB: This has to be done before creating the WorkerPrivate, because it will// attempt to use static variables that are initialized in the RuntimeService// constructor.RuntimeService*runtimeService;if(!parent){runtimeService=RuntimeService::GetOrCreateService();if(!runtimeService){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}}else{runtimeService=RuntimeService::GetService();}MOZ_ASSERT(runtimeService);RefPtr<WorkerPrivate>worker=newWorkerPrivate(parent,aScriptURL,aIsChromeWorker,aWorkerType,aWorkerName,aServiceWorkerScope,*aLoadInfo);// Gecko contexts always have an explicitly-set default locale (set by// XPJSRuntime::Initialize for the main thread, set by// WorkerThreadPrimaryRunnable::Run for workers just before running worker// code), so this is never SpiderMonkey's builtin default locale.JS::UniqueCharsdefaultLocale=JS_GetDefaultLocale(aCx);if(NS_WARN_IF(!defaultLocale)){aRv.Throw(NS_ERROR_UNEXPECTED);returnnullptr;}worker->mDefaultLocale=Move(defaultLocale);if(!runtimeService->RegisterWorker(worker)){aRv.Throw(NS_ERROR_UNEXPECTED);returnnullptr;}worker->EnableDebugger();MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());RefPtr<CompileScriptRunnable>compiler=newCompileScriptRunnable(worker,aScriptURL);if(!compiler->Dispatch()){aRv.Throw(NS_ERROR_UNEXPECTED);returnnullptr;}worker->mSelfRef=worker;returnworker.forget();}// staticnsresultWorkerPrivate::GetLoadInfo(JSContext*aCx,nsPIDOMWindowInner*aWindow,WorkerPrivate*aParent,constnsAString&aScriptURL,boolaIsChromeWorker,LoadGroupBehavioraLoadGroupBehavior,WorkerTypeaWorkerType,WorkerLoadInfo*aLoadInfo){usingnamespacemozilla::dom::workers::scriptloader;MOZ_ASSERT(aCx);MOZ_ASSERT_IF(NS_IsMainThread(),aCx==nsContentUtils::GetCurrentJSContext());if(aWindow){AssertIsOnMainThread();}WorkerLoadInfoloadInfo;nsresultrv;if(aParent){aParent->AssertIsOnWorkerThread();// If the parent is going away give up now.StatusparentStatus;{MutexAutoLocklock(aParent->mMutex);parentStatus=aParent->mStatus;}if(parentStatus>Running){returnNS_ERROR_FAILURE;}// Passing a pointer to our stack loadInfo is safe here because this// method uses a sync runnable to get the channel from the main thread.rv=ChannelFromScriptURLWorkerThread(aCx,aParent,aScriptURL,loadInfo);if(NS_FAILED(rv)){MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));returnrv;}// Now that we've spun the loop there's no guarantee that our parent is// still alive. We may have received control messages initiating shutdown.{MutexAutoLocklock(aParent->mMutex);parentStatus=aParent->mStatus;}if(parentStatus>Running){MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));returnNS_ERROR_FAILURE;}loadInfo.mDomain=aParent->Domain();loadInfo.mFromWindow=aParent->IsFromWindow();loadInfo.mWindowID=aParent->WindowID();loadInfo.mStorageAllowed=aParent->IsStorageAllowed();loadInfo.mOriginAttributes=aParent->GetOriginAttributes();loadInfo.mServiceWorkersTestingInWindow=aParent->ServiceWorkersTestingInWindow();}else{AssertIsOnMainThread();// Make sure that the IndexedDatabaseManager is set upUnused<<NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());nsIScriptSecurityManager*ssm=nsContentUtils::GetSecurityManager();MOZ_ASSERT(ssm);boolisChrome=nsContentUtils::IsSystemCaller(aCx);// First check to make sure the caller has permission to make a privileged// worker if they called the ChromeWorker/ChromeSharedWorker constructor.if(aIsChromeWorker&&!isChrome){returnNS_ERROR_DOM_SECURITY_ERR;}// Chrome callers (whether creating a ChromeWorker or Worker) always get the// system principal here as they're allowed to load anything. The script// loader will refuse to run any script that does not also have the system// principal.if(isChrome){rv=ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal));NS_ENSURE_SUCCESS(rv,rv);loadInfo.mPrincipalIsSystem=true;}// See if we're being called from a window.nsCOMPtr<nsPIDOMWindowInner>globalWindow=aWindow;if(!globalWindow){nsCOMPtr<nsIScriptGlobalObject>scriptGlobal=nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));if(scriptGlobal){globalWindow=do_QueryInterface(scriptGlobal);MOZ_ASSERT(globalWindow);}}nsCOMPtr<nsIDocument>document;if(globalWindow){// Only use the current inner window, and only use it if the caller can// access it.if(nsPIDOMWindowOuter*outerWindow=globalWindow->GetOuterWindow()){loadInfo.mWindow=outerWindow->GetCurrentInnerWindow();// TODO: fix this for SharedWorkers with multiple documents (bug 1177935)loadInfo.mServiceWorkersTestingInWindow=outerWindow->GetServiceWorkersTestingEnabled();}if(!loadInfo.mWindow||(globalWindow!=loadInfo.mWindow&&!nsContentUtils::CanCallerAccess(loadInfo.mWindow))){returnNS_ERROR_DOM_SECURITY_ERR;}nsCOMPtr<nsIScriptGlobalObject>sgo=do_QueryInterface(loadInfo.mWindow);MOZ_ASSERT(sgo);loadInfo.mScriptContext=sgo->GetContext();NS_ENSURE_TRUE(loadInfo.mScriptContext,NS_ERROR_FAILURE);// If we're called from a window then we can dig out the principal and URI// from the document.document=loadInfo.mWindow->GetExtantDoc();NS_ENSURE_TRUE(document,NS_ERROR_FAILURE);loadInfo.mBaseURI=document->GetDocBaseURI();loadInfo.mLoadGroup=document->GetDocumentLoadGroup();NS_ENSURE_TRUE(loadInfo.mLoadGroup,NS_ERROR_FAILURE);// Use the document's NodePrincipal as our principal if we're not being// called from chrome.if(!loadInfo.mPrincipal){loadInfo.mPrincipal=document->NodePrincipal();NS_ENSURE_TRUE(loadInfo.mPrincipal,NS_ERROR_FAILURE);// We use the document's base domain to limit the number of workers// each domain can create. For sandboxed documents, we use the domain// of their first non-sandboxed document, walking up until we find// one. If we can't find one, we fall back to using the GUID of the// null principal as the base domain.if(document->GetSandboxFlags()&SANDBOXED_ORIGIN){nsCOMPtr<nsIDocument>tmpDoc=document;do{tmpDoc=tmpDoc->GetParentDocument();}while(tmpDoc&&tmpDoc->GetSandboxFlags()&SANDBOXED_ORIGIN);if(tmpDoc){// There was an unsandboxed ancestor, yay!nsCOMPtr<nsIPrincipal>tmpPrincipal=tmpDoc->NodePrincipal();rv=tmpPrincipal->GetBaseDomain(loadInfo.mDomain);NS_ENSURE_SUCCESS(rv,rv);}else{// No unsandboxed ancestor, use our GUID.rv=loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);NS_ENSURE_SUCCESS(rv,rv);}}else{// Document creating the worker is not sandboxed.rv=loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);NS_ENSURE_SUCCESS(rv,rv);}}NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,loadInfo.mPrincipal),NS_ERROR_FAILURE);nsCOMPtr<nsIPermissionManager>permMgr=do_GetService(NS_PERMISSIONMANAGER_CONTRACTID,&rv);NS_ENSURE_SUCCESS(rv,rv);uint32_tperm;rv=permMgr->TestPermissionFromPrincipal(loadInfo.mPrincipal,"systemXHR",&perm);NS_ENSURE_SUCCESS(rv,rv);loadInfo.mXHRParamsAllowed=perm==nsIPermissionManager::ALLOW_ACTION;loadInfo.mFromWindow=true;loadInfo.mWindowID=globalWindow->WindowID();nsContentUtils::StorageAccessaccess=nsContentUtils::StorageAllowedForWindow(globalWindow);loadInfo.mStorageAllowed=access>nsContentUtils::StorageAccess::eDeny;loadInfo.mOriginAttributes=nsContentUtils::GetOriginAttributes(document);}else{// Not a windowMOZ_ASSERT(isChrome);// We're being created outside of a window. Need to figure out the script// that is creating us in order for us to use relative URIs later on.JS::AutoFilenamefileName;if(JS::DescribeScriptedCaller(aCx,&fileName)){// In most cases, fileName is URI. In a few other cases// (e.g. xpcshell), fileName is a file path. Ideally, we would// prefer testing whether fileName parses as an URI and fallback// to file path in case of error, but Windows file paths have// the interesting property that they can be parsed as bogus// URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",// hostname "Windows", path "Tmp"), which defeats this algorithm.// Therefore, we adopt the opposite convention.nsCOMPtr<nsIFile>scriptFile=do_CreateInstance("@mozilla.org/file/local;1",&rv);if(NS_FAILED(rv)){returnrv;}rv=scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get()));if(NS_SUCCEEDED(rv)){rv=NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI),scriptFile);}if(NS_FAILED(rv)){// As expected, fileName is not a path, so proceed with// a uri.rv=NS_NewURI(getter_AddRefs(loadInfo.mBaseURI),fileName.get());}if(NS_FAILED(rv)){returnrv;}}loadInfo.mXHRParamsAllowed=true;loadInfo.mFromWindow=false;loadInfo.mWindowID=UINT64_MAX;loadInfo.mStorageAllowed=true;loadInfo.mOriginAttributes=OriginAttributes();}MOZ_ASSERT(loadInfo.mPrincipal);MOZ_ASSERT(isChrome||!loadInfo.mDomain.IsEmpty());if(!loadInfo.mLoadGroup||aLoadGroupBehavior==OverrideLoadGroup){OverrideLoadInfoLoadGroup(loadInfo);}MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,loadInfo.mPrincipal));// Top level workers' main script use the document charset for the script// uri encoding.booluseDefaultEncoding=false;rv=ChannelFromScriptURLMainThread(loadInfo.mPrincipal,loadInfo.mBaseURI,document,loadInfo.mLoadGroup,aScriptURL,ContentPolicyType(aWorkerType),useDefaultEncoding,getter_AddRefs(loadInfo.mChannel));NS_ENSURE_SUCCESS(rv,rv);rv=NS_GetFinalChannelURI(loadInfo.mChannel,getter_AddRefs(loadInfo.mResolvedScriptURI));NS_ENSURE_SUCCESS(rv,rv);rv=loadInfo.SetPrincipalFromChannel(loadInfo.mChannel);NS_ENSURE_SUCCESS(rv,rv);}MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid());aLoadInfo->StealFrom(loadInfo);returnNS_OK;}// staticvoidWorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo&aLoadInfo){MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);aLoadInfo.mInterfaceRequestor=newWorkerLoadInfo::InterfaceRequestor(aLoadInfo.mPrincipal,aLoadInfo.mLoadGroup);aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup);// NOTE: this defaults the load context to:// - private browsing = false// - content = true// - use remote tabs = falsensCOMPtr<nsILoadGroup>loadGroup=do_CreateInstance(NS_LOADGROUP_CONTRACTID);nsresultrv=loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);MOZ_ALWAYS_SUCCEEDS(rv);aLoadInfo.mLoadGroup=loadGroup.forget();MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup,aLoadInfo.mPrincipal));}voidWorkerPrivate::DoRunLoop(JSContext*aCx){AssertIsOnWorkerThread();MOZ_ASSERT(mThread);{MutexAutoLocklock(mMutex);mJSContext=aCx;MOZ_ASSERT(mStatus==Pending);mStatus=Running;}// Now that we've done that, we can go ahead and set up our AutoJSAPI. We// can't before this point, because it can't find the right JSContext before// then, since it gets it from our mJSContext.AutoJSAPIjsapi;jsapi.Init();MOZ_ASSERT(jsapi.cx()==aCx);EnableMemoryReporter();InitializeGCTimers();Maybe<JSAutoCompartment>workerCompartment;for(;;){StatuscurrentStatus,previousStatus;booldebuggerRunnablesPending=false;boolnormalRunnablesPending=false;{MutexAutoLocklock(mMutex);previousStatus=mStatus;while(mControlQueue.IsEmpty()&&!(debuggerRunnablesPending=!mDebuggerQueue.IsEmpty())&&!(normalRunnablesPending=NS_HasPendingEvents(mThread))){WaitForWorkerEvents();}autoresult=ProcessAllControlRunnablesLocked();if(result!=ProcessAllControlRunnablesResult::Nothing){// NB: There's no JS on the stack here, so Abort vs MayContinue is// irrelevant// The state of the world may have changed, recheck it.normalRunnablesPending=NS_HasPendingEvents(mThread);// The debugger queue doesn't get cleared, so we can ignore that.}currentStatus=mStatus;}// if all holders are done then we can kill this thread.if(currentStatus!=Running&&!HasActiveHolders()){// If we just changed status, we must schedule the current runnables.if(previousStatus!=Running&¤tStatus!=Killing){NotifyInternal(aCx,Killing);MOZ_ASSERT(!JS_IsExceptionPending(aCx));#ifdef DEBUG{MutexAutoLocklock(mMutex);currentStatus=mStatus;}MOZ_ASSERT(currentStatus==Killing);#elsecurrentStatus=Killing;#endif}// If we're supposed to die then we should exit the loop.if(currentStatus==Killing){// Flush uncaught rejections immediately, without// waiting for a next tick.PromiseDebugging::FlushUncaughtRejections();ShutdownGCTimers();DisableMemoryReporter();{MutexAutoLocklock(mMutex);mStatus=Dead;mJSContext=nullptr;}// After mStatus is set to Dead there can be no more// WorkerControlRunnables so no need to lock here.if(!mControlQueue.IsEmpty()){WorkerControlRunnable*runnable;while(mControlQueue.Pop(runnable)){runnable->Cancel();runnable->Release();}}// Unroot the globalsmScope=nullptr;mDebuggerScope=nullptr;return;}}if(debuggerRunnablesPending||normalRunnablesPending){// Start the periodic GC timer if it is not already running.SetGCTimerMode(PeriodicTimer);}if(debuggerRunnablesPending){WorkerRunnable*runnable;{MutexAutoLocklock(mMutex);mDebuggerQueue.Pop(runnable);debuggerRunnablesPending=!mDebuggerQueue.IsEmpty();}MOZ_ASSERT(runnable);static_cast<nsIRunnable*>(runnable)->Run();runnable->Release();// Flush the promise queue.Promise::PerformWorkerDebuggerMicroTaskCheckpoint();if(debuggerRunnablesPending){WorkerDebuggerGlobalScope*globalScope=DebuggerGlobalScope();MOZ_ASSERT(globalScope);// Now *might* be a good time to GC. Let the JS engine make the decision.JSAutoCompartmentac(aCx,globalScope->GetGlobalJSObject());JS_MaybeGC(aCx);}}elseif(normalRunnablesPending){// Process a single runnable from the main queue.NS_ProcessNextEvent(mThread,false);normalRunnablesPending=NS_HasPendingEvents(mThread);if(normalRunnablesPending&&GlobalScope()){// Now *might* be a good time to GC. Let the JS engine make the decision.JSAutoCompartmentac(aCx,GlobalScope()->GetGlobalJSObject());JS_MaybeGC(aCx);}}if(!debuggerRunnablesPending&&!normalRunnablesPending){// Both the debugger event queue and the normal event queue has been// exhausted, cancel the periodic GC timer and schedule the idle GC timer.SetGCTimerMode(IdleTimer);}// If the worker thread is spamming the main thread faster than it can// process the work, then pause the worker thread until the MT catches// up.if(mMainThreadThrottledEventQueue&&mMainThreadThrottledEventQueue->Length()>5000){mMainThreadThrottledEventQueue->AwaitIdle();}}MOZ_CRASH("Shouldn't get here!");}voidWorkerPrivate::OnProcessNextEvent(){AssertIsOnWorkerThread();uint32_trecursionDepth=CycleCollectedJSContext::Get()->RecursionDepth();MOZ_ASSERT(recursionDepth);// Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.// However, it's possible that non-worker C++ could spin its own nested event// loop, and in that case we must ensure that we continue to process control// runnables here.if(recursionDepth>1&&mSyncLoopStack.Length()<recursionDepth-1){Unused<<ProcessAllControlRunnables();// There's no running JS, and no state to revalidate, so we can ignore the// return value.}}voidWorkerPrivate::AfterProcessNextEvent(){AssertIsOnWorkerThread();MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth());}voidWorkerPrivate::MaybeDispatchLoadFailedRunnable(){AssertIsOnWorkerThread();nsCOMPtr<nsIRunnable>runnable=StealLoadFailedAsyncRunnable();if(!runnable){return;}MOZ_ALWAYS_SUCCEEDS(DispatchToMainThread(runnable.forget()));}nsIEventTarget*WorkerPrivate::MainThreadEventTarget(){returnmMainThreadEventTarget;}nsresultWorkerPrivate::DispatchToMainThread(nsIRunnable*aRunnable,uint32_taFlags){nsCOMPtr<nsIRunnable>r=aRunnable;returnDispatchToMainThread(r.forget(),aFlags);}nsresultWorkerPrivate::DispatchToMainThread(already_AddRefed<nsIRunnable>aRunnable,uint32_taFlags){nsCOMPtr<nsIRunnable>runnable=aRunnable;if(nsCOMPtr<nsINamed>named=do_QueryInterface(runnable)){named->SetName("WorkerRunnable");}returnmMainThreadEventTarget->Dispatch(runnable.forget(),aFlags);}nsIEventTarget*WorkerPrivate::ControlEventTarget(){returnmWorkerControlEventTarget;}voidWorkerPrivate::InitializeGCTimers(){AssertIsOnWorkerThread();// We need a timer for GC. The basic plan is to run a non-shrinking GC// periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.// Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to// run a shrinking GC. If the worker receives more messages then the short// timer is canceled and the periodic timer resumes.mGCTimer=do_CreateInstance(NS_TIMER_CONTRACTID);MOZ_ASSERT(mGCTimer);mPeriodicGCTimerRunning=false;mIdleGCTimerRunning=false;}voidWorkerPrivate::SetGCTimerMode(GCTimerModeaMode){AssertIsOnWorkerThread();MOZ_ASSERT(mGCTimer);if((aMode==PeriodicTimer&&mPeriodicGCTimerRunning)||(aMode==IdleTimer&&mIdleGCTimerRunning)){return;}MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());mPeriodicGCTimerRunning=false;mIdleGCTimerRunning=false;LOG(WorkerLog(),("Worker %p canceled GC timer because %s\n",this,aMode==PeriodicTimer?"periodic":aMode==IdleTimer?"idle":"none"));if(aMode==NoTimer){return;}MOZ_ASSERT(aMode==PeriodicTimer||aMode==IdleTimer);uint32_tdelay=0;int16_ttype=nsITimer::TYPE_ONE_SHOT;nsTimerCallbackFunccallback=nullptr;constchar*name=nullptr;if(aMode==PeriodicTimer){delay=PERIODIC_GC_TIMER_DELAY_SEC*1000;type=nsITimer::TYPE_REPEATING_SLACK;callback=PeriodicGCTimerCallback;name="dom::workers::PeriodicGCTimerCallback";}else{delay=IDLE_GC_TIMER_DELAY_SEC*1000;type=nsITimer::TYPE_ONE_SHOT;callback=IdleGCTimerCallback;name="dom::workers::IdleGCTimerCallback";}MOZ_ALWAYS_SUCCEEDS(mGCTimer->SetTarget(mWorkerControlEventTarget));MOZ_ALWAYS_SUCCEEDS(mGCTimer->InitWithNamedFuncCallback(callback,this,delay,type,name));if(aMode==PeriodicTimer){LOG(WorkerLog(),("Worker %p scheduled periodic GC timer\n",this));mPeriodicGCTimerRunning=true;}else{LOG(WorkerLog(),("Worker %p scheduled idle GC timer\n",this));mIdleGCTimerRunning=true;}}voidWorkerPrivate::ShutdownGCTimers(){AssertIsOnWorkerThread();MOZ_ASSERT(mGCTimer);// Always make sure the timer is canceled.MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());LOG(WorkerLog(),("Worker %p killed the GC timer\n",this));mGCTimer=nullptr;mPeriodicGCTimerRunning=false;mIdleGCTimerRunning=false;}boolWorkerPrivate::InterruptCallback(JSContext*aCx){AssertIsOnWorkerThread();MOZ_ASSERT(!JS_IsExceptionPending(aCx));boolmayContinue=true;boolscheduledIdleGC=false;for(;;){// Run all control events now.autoresult=ProcessAllControlRunnables();if(result==ProcessAllControlRunnablesResult::Abort){mayContinue=false;}boolmayFreeze=mFrozen;if(mayFreeze){MutexAutoLocklock(mMutex);mayFreeze=mStatus<=Running;}if(!mayContinue||!mayFreeze){break;}// Cancel the periodic GC timer here before freezing. The idle GC timer// will clean everything up once it runs.if(!scheduledIdleGC){SetGCTimerMode(IdleTimer);scheduledIdleGC=true;}while((mayContinue=MayContinueRunning())){MutexAutoLocklock(mMutex);if(!mControlQueue.IsEmpty()){break;}WaitForWorkerEvents(PR_MillisecondsToInterval(UINT32_MAX));}}if(!mayContinue){// We want only uncatchable exceptions here.NS_ASSERTION(!JS_IsExceptionPending(aCx),"Should not have an exception set here!");returnfalse;}// Make sure the periodic timer gets turned back on here.SetGCTimerMode(PeriodicTimer);returntrue;}boolWorkerPrivate::IsOnCurrentThread(){// May be called on any thread!MOZ_ASSERT(mPRThread);returnPR_GetCurrentThread()==mPRThread;}voidWorkerPrivate::ScheduleDeletion(WorkerRanOrNotaRanOrNot){AssertIsOnWorkerThread();MOZ_ASSERT(mChildWorkers.IsEmpty());MOZ_ASSERT(mSyncLoopStack.IsEmpty());MOZ_ASSERT(!mPendingEventQueueClearing);ClearMainEventQueue(aRanOrNot);#ifdef DEBUGif(WorkerRan==aRanOrNot){nsIThread*currentThread=NS_GetCurrentThread();MOZ_ASSERT(currentThread);MOZ_ASSERT(!NS_HasPendingEvents(currentThread));}#endifif(WorkerPrivate*parent=GetParent()){RefPtr<WorkerFinishedRunnable>runnable=newWorkerFinishedRunnable(parent,this);if(!runnable->Dispatch()){NS_WARNING("Failed to dispatch runnable!");}}else{RefPtr<TopLevelWorkerFinishedRunnable>runnable=newTopLevelWorkerFinishedRunnable(this);if(NS_FAILED(DispatchToMainThread(runnable.forget()))){NS_WARNING("Failed to dispatch runnable!");}}}boolWorkerPrivate::CollectRuntimeStats(JS::RuntimeStats*aRtStats,boolaAnonymize){AssertIsOnWorkerThread();NS_ASSERTION(aRtStats,"Null RuntimeStats!");NS_ASSERTION(mJSContext,"This must never be null!");returnJS::CollectRuntimeStats(mJSContext,aRtStats,nullptr,aAnonymize);}voidWorkerPrivate::EnableMemoryReporter(){AssertIsOnWorkerThread();MOZ_ASSERT(!mMemoryReporter);// No need to lock here since the main thread can't race until we've// successfully registered the reporter.mMemoryReporter=newMemoryReporter(this);if(NS_FAILED(RegisterWeakAsyncMemoryReporter(mMemoryReporter))){NS_WARNING("Failed to register memory reporter!");// No need to lock here since a failed registration means our memory// reporter can't start running. Just clean up.mMemoryReporter=nullptr;}}voidWorkerPrivate::DisableMemoryReporter(){AssertIsOnWorkerThread();RefPtr<MemoryReporter>memoryReporter;{// Mutex protectes MemoryReporter::mWorkerPrivate which is cleared by// MemoryReporter::Disable() below.MutexAutoLocklock(mMutex);// There is nothing to do here if the memory reporter was never successfully// registered.if(!mMemoryReporter){return;}// We don't need this set any longer. Swap it out so that we can unregister// below.mMemoryReporter.swap(memoryReporter);// Next disable the memory reporter so that the main thread stops trying to// signal us.memoryReporter->Disable();}// Finally unregister the memory reporter.if(NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))){NS_WARNING("Failed to unregister memory reporter!");}}voidWorkerPrivate::WaitForWorkerEvents(PRIntervalTimeaInterval){AssertIsOnWorkerThread();mMutex.AssertCurrentThreadOwns();// Wait for a worker event.mCondVar.Wait(aInterval);}WorkerPrivate::ProcessAllControlRunnablesResultWorkerPrivate::ProcessAllControlRunnablesLocked(){AssertIsOnWorkerThread();mMutex.AssertCurrentThreadOwns();autoresult=ProcessAllControlRunnablesResult::Nothing;for(;;){WorkerControlRunnable*event;if(!mControlQueue.Pop(event)){break;}MutexAutoUnlockunlock(mMutex);MOZ_ASSERT(event);if(NS_FAILED(static_cast<nsIRunnable*>(event)->Run())){result=ProcessAllControlRunnablesResult::Abort;}if(result==ProcessAllControlRunnablesResult::Nothing){// We ran at least one thing.result=ProcessAllControlRunnablesResult::MayContinue;}event->Release();}returnresult;}voidWorkerPrivate::ClearMainEventQueue(WorkerRanOrNotaRanOrNot){AssertIsOnWorkerThread();MOZ_ASSERT(mSyncLoopStack.IsEmpty());MOZ_ASSERT(!mCancelAllPendingRunnables);mCancelAllPendingRunnables=true;if(WorkerNeverRan==aRanOrNot){for(uint32_tcount=mPreStartRunnables.Length(),index=0;index<count;index++){RefPtr<WorkerRunnable>runnable=mPreStartRunnables[index].forget();static_cast<nsIRunnable*>(runnable.get())->Run();}}else{nsIThread*currentThread=NS_GetCurrentThread();MOZ_ASSERT(currentThread);NS_ProcessPendingEvents(currentThread);}MOZ_ASSERT(mCancelAllPendingRunnables);mCancelAllPendingRunnables=false;}voidWorkerPrivate::ClearDebuggerEventQueue(){while(!mDebuggerQueue.IsEmpty()){WorkerRunnable*runnable;mDebuggerQueue.Pop(runnable);// It should be ok to simply release the runnable, without running it.runnable->Release();}}boolWorkerPrivate::FreezeInternal(){AssertIsOnWorkerThread();NS_ASSERTION(!mFrozen,"Already frozen!");mFrozen=true;for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->Freeze(nullptr);}returntrue;}boolWorkerPrivate::ThawInternal(){AssertIsOnWorkerThread();NS_ASSERTION(mFrozen,"Not yet frozen!");for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->Thaw(nullptr);}mFrozen=false;returntrue;}voidWorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback&cb){for(uint32_ti=0;i<mTimeouts.Length();++i){TimeoutInfo*tmp=mTimeouts[i];NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHandler)}}voidWorkerPrivate::UnlinkTimeouts(){mTimeouts.Clear();}boolWorkerPrivate::ModifyBusyCountFromWorker(boolaIncrease){AssertIsOnWorkerThread();{MutexAutoLocklock(mMutex);// If we're in shutdown then the busy count is no longer being considered so// just return now.if(mStatus>=Killing){returntrue;}}RefPtr<ModifyBusyCountRunnable>runnable=newModifyBusyCountRunnable(this,aIncrease);returnrunnable->Dispatch();}boolWorkerPrivate::AddChildWorker(ParentType*aChildWorker){AssertIsOnWorkerThread();#ifdef DEBUG{StatuscurrentStatus;{MutexAutoLocklock(mMutex);currentStatus=mStatus;}MOZ_ASSERT(currentStatus==Running);}#endifNS_ASSERTION(!mChildWorkers.Contains(aChildWorker),"Already know about this one!");mChildWorkers.AppendElement(aChildWorker);returnmChildWorkers.Length()==1?ModifyBusyCountFromWorker(true):true;}voidWorkerPrivate::RemoveChildWorker(ParentType*aChildWorker){AssertIsOnWorkerThread();NS_ASSERTION(mChildWorkers.Contains(aChildWorker),"Didn't know about this one!");mChildWorkers.RemoveElement(aChildWorker);if(mChildWorkers.IsEmpty()&&!ModifyBusyCountFromWorker(false)){NS_WARNING("Failed to modify busy count!");}}boolWorkerPrivate::AddHolder(WorkerHolder*aHolder,StatusaFailStatus){AssertIsOnWorkerThread();{MutexAutoLocklock(mMutex);if(mStatus>=aFailStatus){returnfalse;}}MOZ_ASSERT(!mHolders.Contains(aHolder),"Already know about this one!");if(aHolder->GetBehavior()==WorkerHolder::PreventIdleShutdownStart){if(!mNumHoldersPreventingShutdownStart&&!ModifyBusyCountFromWorker(true)){returnfalse;}mNumHoldersPreventingShutdownStart+=1;}mHolders.AppendElement(aHolder);returntrue;}voidWorkerPrivate::RemoveHolder(WorkerHolder*aHolder){AssertIsOnWorkerThread();MOZ_ASSERT(mHolders.Contains(aHolder),"Didn't know about this one!");mHolders.RemoveElement(aHolder);if(aHolder->GetBehavior()==WorkerHolder::PreventIdleShutdownStart){mNumHoldersPreventingShutdownStart-=1;if(!mNumHoldersPreventingShutdownStart&&!ModifyBusyCountFromWorker(false)){NS_WARNING("Failed to modify busy count!");}}}voidWorkerPrivate::NotifyHolders(JSContext*aCx,StatusaStatus){AssertIsOnWorkerThread();MOZ_ASSERT(!JS_IsExceptionPending(aCx));NS_ASSERTION(aStatus>Running,"Bad status!");if(aStatus>=Closing){CancelAllTimeouts();}nsTObserverArray<WorkerHolder*>::ForwardIteratoriter(mHolders);while(iter.HasMore()){WorkerHolder*holder=iter.GetNext();if(!holder->Notify(aStatus)){NS_WARNING("Failed to notify holder!");}MOZ_ASSERT(!JS_IsExceptionPending(aCx));}AutoTArray<ParentType*,10>children;children.AppendElements(mChildWorkers);for(uint32_tindex=0;index<children.Length();index++){if(!children[index]->Notify(aStatus)){NS_WARNING("Failed to notify child worker!");}}}voidWorkerPrivate::CancelAllTimeouts(){AssertIsOnWorkerThread();LOG(TimeoutsLog(),("Worker %p CancelAllTimeouts.\n",this));if(mTimerRunning){NS_ASSERTION(mTimer&&mTimerRunnable,"Huh?!");NS_ASSERTION(!mTimeouts.IsEmpty(),"Huh?!");if(NS_FAILED(mTimer->Cancel())){NS_WARNING("Failed to cancel timer!");}for(uint32_tindex=0;index<mTimeouts.Length();index++){mTimeouts[index]->mCanceled=true;}// If mRunningExpiredTimeouts, then the fact that they are all canceled now// means that the currently executing RunExpiredTimeouts will deal with// them. Otherwise, we need to clean them up ourselves.if(!mRunningExpiredTimeouts){mTimeouts.Clear();ModifyBusyCountFromWorker(false);}// Set mTimerRunning false even if mRunningExpiredTimeouts is true, so that// if we get reentered under this same RunExpiredTimeouts call we don't// assert above that !mTimeouts().IsEmpty(), because that's clearly false// now.mTimerRunning=false;}#ifdef DEBUGelseif(!mRunningExpiredTimeouts){NS_ASSERTION(mTimeouts.IsEmpty(),"Huh?!");}#endifmTimer=nullptr;mTimerRunnable=nullptr;}already_AddRefed<nsIEventTarget>WorkerPrivate::CreateNewSyncLoop(StatusaFailStatus){AssertIsOnWorkerThread();{MutexAutoLocklock(mMutex);if(mStatus>=aFailStatus){returnnullptr;}}nsCOMPtr<nsIThreadInternal>thread=do_QueryInterface(NS_GetCurrentThread());MOZ_ASSERT(thread);nsCOMPtr<nsIEventTarget>realEventTarget;MOZ_ALWAYS_SUCCEEDS(thread->PushEventQueue(getter_AddRefs(realEventTarget)));RefPtr<EventTarget>workerEventTarget=newEventTarget(this,realEventTarget);{// Modifications must be protected by mMutex in DEBUG builds, see comment// about mSyncLoopStack in WorkerPrivate.h.#ifdef DEBUGMutexAutoLocklock(mMutex);#endifmSyncLoopStack.AppendElement(newSyncLoopInfo(workerEventTarget));}returnworkerEventTarget.forget();}boolWorkerPrivate::RunCurrentSyncLoop(){AssertIsOnWorkerThread();JSContext*cx=GetJSContext();MOZ_ASSERT(cx);// This should not change between now and the time we finish running this sync// loop.uint32_tcurrentLoopIndex=mSyncLoopStack.Length()-1;SyncLoopInfo*loopInfo=mSyncLoopStack[currentLoopIndex];MOZ_ASSERT(loopInfo);MOZ_ASSERT(!loopInfo->mHasRun);MOZ_ASSERT(!loopInfo->mCompleted);#ifdef DEBUGloopInfo->mHasRun=true;#endifwhile(!loopInfo->mCompleted){boolnormalRunnablesPending=false;// Don't block with the periodic GC timer running.if(!NS_HasPendingEvents(mThread)){SetGCTimerMode(IdleTimer);}// Wait for something to do.{MutexAutoLocklock(mMutex);for(;;){while(mControlQueue.IsEmpty()&&!normalRunnablesPending&&!(normalRunnablesPending=NS_HasPendingEvents(mThread))){WaitForWorkerEvents();}autoresult=ProcessAllControlRunnablesLocked();if(result!=ProcessAllControlRunnablesResult::Nothing){// XXXkhuey how should we handle Abort here? See Bug 1003730.// The state of the world may have changed. Recheck it.normalRunnablesPending=NS_HasPendingEvents(mThread);// NB: If we processed a NotifyRunnable, we might have run// non-control runnables, one of which may have shut down the// sync loop.if(loopInfo->mCompleted){break;}}// If we *didn't* run any control runnables, this should be unchanged.MOZ_ASSERT(!loopInfo->mCompleted);if(normalRunnablesPending){break;}}}if(normalRunnablesPending){// Make sure the periodic timer is running before we continue.SetGCTimerMode(PeriodicTimer);MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread,false));// Now *might* be a good time to GC. Let the JS engine make the decision.if(JS::CurrentGlobalOrNull(cx)){JS_MaybeGC(cx);}}}// Make sure that the stack didn't change underneath us.MOZ_ASSERT(mSyncLoopStack[currentLoopIndex]==loopInfo);returnDestroySyncLoop(currentLoopIndex);}boolWorkerPrivate::DestroySyncLoop(uint32_taLoopIndex,nsIThreadInternal*aThread){MOZ_ASSERT(!mSyncLoopStack.IsEmpty());MOZ_ASSERT(mSyncLoopStack.Length()-1==aLoopIndex);if(!aThread){aThread=mThread;}// We're about to delete the loop, stash its event target and result.SyncLoopInfo*loopInfo=mSyncLoopStack[aLoopIndex];nsIEventTarget*nestedEventTarget=loopInfo->mEventTarget->GetWeakNestedEventTarget();MOZ_ASSERT(nestedEventTarget);boolresult=loopInfo->mResult;{// Modifications must be protected by mMutex in DEBUG builds, see comment// about mSyncLoopStack in WorkerPrivate.h.#ifdef DEBUGMutexAutoLocklock(mMutex);#endif// This will delete |loopInfo|!mSyncLoopStack.RemoveElementAt(aLoopIndex);}MOZ_ALWAYS_SUCCEEDS(aThread->PopEventQueue(nestedEventTarget));if(mSyncLoopStack.IsEmpty()&&mPendingEventQueueClearing){mPendingEventQueueClearing=false;ClearMainEventQueue(WorkerRan);}returnresult;}voidWorkerPrivate::StopSyncLoop(nsIEventTarget*aSyncLoopTarget,boolaResult){AssertIsOnWorkerThread();AssertValidSyncLoop(aSyncLoopTarget);MOZ_ASSERT(!mSyncLoopStack.IsEmpty());for(uint32_tindex=mSyncLoopStack.Length();index>0;index--){nsAutoPtr<SyncLoopInfo>&loopInfo=mSyncLoopStack[index-1];MOZ_ASSERT(loopInfo);MOZ_ASSERT(loopInfo->mEventTarget);if(loopInfo->mEventTarget==aSyncLoopTarget){// Can't assert |loop->mHasRun| here because dispatch failures can cause// us to bail out early.MOZ_ASSERT(!loopInfo->mCompleted);loopInfo->mResult=aResult;loopInfo->mCompleted=true;loopInfo->mEventTarget->Disable();return;}MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget,aSyncLoopTarget));}MOZ_CRASH("Unknown sync loop!");}#ifdef DEBUGvoidWorkerPrivate::AssertValidSyncLoop(nsIEventTarget*aSyncLoopTarget){MOZ_ASSERT(aSyncLoopTarget);EventTarget*workerTarget;nsresultrv=aSyncLoopTarget->QueryInterface(kDEBUGWorkerEventTargetIID,reinterpret_cast<void**>(&workerTarget));MOZ_ASSERT(NS_SUCCEEDED(rv));MOZ_ASSERT(workerTarget);boolvalid=false;{MutexAutoLocklock(mMutex);for(uint32_tindex=0;index<mSyncLoopStack.Length();index++){nsAutoPtr<SyncLoopInfo>&loopInfo=mSyncLoopStack[index];MOZ_ASSERT(loopInfo);MOZ_ASSERT(loopInfo->mEventTarget);if(loopInfo->mEventTarget==aSyncLoopTarget){valid=true;break;}MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget,aSyncLoopTarget));}}MOZ_ASSERT(valid);}#endifvoidWorkerPrivate::PostMessageToParentInternal(JSContext*aCx,JS::Handle<JS::Value>aMessage,constSequence<JSObject*>&aTransferable,ErrorResult&aRv){AssertIsOnWorkerThread();JS::Rooted<JS::Value>transferable(aCx,JS::UndefinedValue());aRv=nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,aTransferable,&transferable);if(NS_WARN_IF(aRv.Failed())){return;}RefPtr<MessageEventRunnable>runnable=newMessageEventRunnable(this,WorkerRunnable::ParentThreadUnchangedBusyCount);UniquePtr<AbstractTimelineMarker>start;UniquePtr<AbstractTimelineMarker>end;RefPtr<TimelineConsumers>timelines=TimelineConsumers::Get();boolisTimelineRecording=timelines&&!timelines->IsEmpty();if(isTimelineRecording){start=MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()?ProfileTimelineWorkerOperationType::SerializeDataOnMainThread:ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,MarkerTracingType::START);}runnable->Write(aCx,aMessage,transferable,JS::CloneDataPolicy(),aRv);if(isTimelineRecording){end=MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()?ProfileTimelineWorkerOperationType::SerializeDataOnMainThread:ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,MarkerTracingType::END);timelines->AddMarkerForAllObservedDocShells(start);timelines->AddMarkerForAllObservedDocShells(end);}if(NS_WARN_IF(aRv.Failed())){return;}if(!runnable->Dispatch()){aRv=NS_ERROR_FAILURE;}}voidWorkerPrivate::EnterDebuggerEventLoop(){AssertIsOnWorkerThread();JSContext*cx=GetJSContext();MOZ_ASSERT(cx);uint32_tcurrentEventLoopLevel=++mDebuggerEventLoopLevel;while(currentEventLoopLevel<=mDebuggerEventLoopLevel){booldebuggerRunnablesPending=false;{MutexAutoLocklock(mMutex);debuggerRunnablesPending=!mDebuggerQueue.IsEmpty();}// Don't block with the periodic GC timer running.if(!debuggerRunnablesPending){SetGCTimerMode(IdleTimer);}// Wait for something to do{MutexAutoLocklock(mMutex);while(mControlQueue.IsEmpty()&&!(debuggerRunnablesPending=!mDebuggerQueue.IsEmpty())){WaitForWorkerEvents();}ProcessAllControlRunnablesLocked();// XXXkhuey should we abort JS on the stack here if we got Abort above?}if(debuggerRunnablesPending){// Start the periodic GC timer if it is not already running.SetGCTimerMode(PeriodicTimer);WorkerRunnable*runnable;{MutexAutoLocklock(mMutex);mDebuggerQueue.Pop(runnable);}MOZ_ASSERT(runnable);static_cast<nsIRunnable*>(runnable)->Run();runnable->Release();// Flush the promise queue.Promise::PerformWorkerDebuggerMicroTaskCheckpoint();// Now *might* be a good time to GC. Let the JS engine make the decision.if(JS::CurrentGlobalOrNull(cx)){JS_MaybeGC(cx);}}}}voidWorkerPrivate::LeaveDebuggerEventLoop(){AssertIsOnWorkerThread();MutexAutoLocklock(mMutex);if(mDebuggerEventLoopLevel>0){--mDebuggerEventLoopLevel;}}voidWorkerPrivate::PostMessageToDebugger(constnsAString&aMessage){mDebugger->PostMessageToDebugger(aMessage);}voidWorkerPrivate::SetDebuggerImmediate(dom::Function&aHandler,ErrorResult&aRv){AssertIsOnWorkerThread();RefPtr<DebuggerImmediateRunnable>runnable=newDebuggerImmediateRunnable(this,aHandler);if(!runnable->Dispatch()){aRv.Throw(NS_ERROR_FAILURE);}}voidWorkerPrivate::ReportErrorToDebugger(constnsAString&aFilename,uint32_taLineno,constnsAString&aMessage){mDebugger->ReportErrorToDebugger(aFilename,aLineno,aMessage);}boolWorkerPrivate::NotifyInternal(JSContext*aCx,StatusaStatus){AssertIsOnWorkerThread();NS_ASSERTION(aStatus>Running&&aStatus<Dead,"Bad status!");RefPtr<EventTarget>eventTarget;// Save the old status and set the new status.StatuspreviousStatus;{MutexAutoLocklock(mMutex);if(mStatus>=aStatus){returntrue;}previousStatus=mStatus;mStatus=aStatus;// Mark parent status as closing immediately to avoid new events being// dispatched after we clear the queue below.if(aStatus==Closing){Close();}eventTarget=mEventTarget;}// Disable the event target, if it exists.if(eventTarget){// Since we'll no longer process events, make sure we no longer allow anyone// to post them. We have to do this without mMutex held, since our mutex// must be acquired *after* the WorkerEventTarget's mutex when they're both// held.eventTarget->Disable();}if(mCrossThreadDispatcher){// Since we'll no longer process events, make sure we no longer allow// anyone to post them. We have to do this without mMutex held, since our// mutex must be acquired *after* mCrossThreadDispatcher's mutex when// they're both held.mCrossThreadDispatcher->Forget();mCrossThreadDispatcher=nullptr;}MOZ_ASSERT(previousStatus!=Pending);// Let all our holders know the new status.NotifyHolders(aCx,aStatus);MOZ_ASSERT(!JS_IsExceptionPending(aCx));// If this is the first time our status has changed then we need to clear the// main event queue.if(previousStatus==Running){// NB: If we're in a sync loop, we can't clear the queue immediately,// because this is the wrong queue. So we have to defer it until later.if(!mSyncLoopStack.IsEmpty()){mPendingEventQueueClearing=true;}else{ClearMainEventQueue(WorkerRan);}}// If the worker script never ran, or failed to compile, we don't need to do// anything else.if(!GlobalScope()){returntrue;}// Don't abort the script.if(aStatus==Closing){returntrue;}MOZ_ASSERT(aStatus==Terminating||aStatus==Canceling||aStatus==Killing);// Always abort the script.returnfalse;}voidWorkerErrorBase::AssignErrorBase(JSErrorBase*aReport){mFilename=NS_ConvertUTF8toUTF16(aReport->filename);mLineNumber=aReport->lineno;mColumnNumber=aReport->column;mErrorNumber=aReport->errorNumber;}voidWorkerErrorNote::AssignErrorNote(JSErrorNotes::Note*aNote){WorkerErrorBase::AssignErrorBase(aNote);xpc::ErrorNote::ErrorNoteToMessageString(aNote,mMessage);}voidWorkerErrorReport::AssignErrorReport(JSErrorReport*aReport){WorkerErrorBase::AssignErrorBase(aReport);xpc::ErrorReport::ErrorReportToMessageString(aReport,mMessage);mLine.Assign(aReport->linebuf(),aReport->linebufLength());mFlags=aReport->flags;MOZ_ASSERT(aReport->exnType>=JSEXN_FIRST&&aReport->exnType<JSEXN_LIMIT);mExnType=JSExnType(aReport->exnType);mMutedError=aReport->isMuted;if(aReport->notes){if(!mNotes.SetLength(aReport->notes->length(),fallible)){return;}size_ti=0;for(auto&¬e:*aReport->notes){mNotes.ElementAt(i).AssignErrorNote(note.get());i++;}}}voidWorkerPrivate::ReportError(JSContext*aCx,JS::ConstUTF8CharsZaToStringResult,JSErrorReport*aReport){AssertIsOnWorkerThread();if(!MayContinueRunning()||mErrorHandlerRecursionCount==2){return;}NS_ASSERTION(mErrorHandlerRecursionCount==0||mErrorHandlerRecursionCount==1,"Bad recursion logic!");JS::Rooted<JS::Value>exn(aCx);if(!JS_GetPendingException(aCx,&exn)){// Probably shouldn't actually happen? But let's go ahead and just use null// for lack of anything better.exn.setNull();}JS_ClearPendingException(aCx);WorkerErrorReportreport;if(aReport){report.AssignErrorReport(aReport);}else{report.mFlags=nsIScriptError::errorFlag|nsIScriptError::exceptionFlag;}if(report.mMessage.IsEmpty()&&aToStringResult){nsDependentCStringtoStringResult(aToStringResult.c_str());if(!AppendUTF8toUTF16(toStringResult,report.mMessage,mozilla::fallible)){// Try again, with only a 1 KB string. Do this infallibly this time.// If the user doesn't have 1 KB to spare we're done anyways.uint32_tindex=std::min(uint32_t(1024),toStringResult.Length());// Drop the last code point that may be cropped.index=RewindToPriorUTF8Codepoint(toStringResult.BeginReading(),index);nsDependentCStringtruncatedToStringResult(aToStringResult.c_str(),index);AppendUTF8toUTF16(truncatedToStringResult,report.mMessage);}}mErrorHandlerRecursionCount++;// Don't want to run the scope's error handler if this is a recursive error or// if we ran out of memory.boolfireAtScope=mErrorHandlerRecursionCount==1&&report.mErrorNumber!=JSMSG_OUT_OF_MEMORY&&JS::CurrentGlobalOrNull(aCx);ReportErrorRunnable::ReportError(aCx,this,fireAtScope,nullptr,report,0,exn);mErrorHandlerRecursionCount--;}// staticvoidWorkerPrivate::ReportErrorToConsole(constchar*aMessage){WorkerPrivate*wp=nullptr;if(!NS_IsMainThread()){wp=GetCurrentThreadWorkerPrivate();}ReportErrorToConsoleRunnable::Report(wp,aMessage);}int32_tWorkerPrivate::SetTimeout(JSContext*aCx,nsIScriptTimeoutHandler*aHandler,int32_taTimeout,boolaIsInterval,ErrorResult&aRv){AssertIsOnWorkerThread();MOZ_ASSERT(aHandler);constint32_ttimerId=mNextTimeoutId++;StatuscurrentStatus;{MutexAutoLocklock(mMutex);currentStatus=mStatus;}// If the worker is trying to call setTimeout/setInterval and the parent// thread has initiated the close process then just silently fail.if(currentStatus>=Closing){aRv.Throw(NS_ERROR_FAILURE);return0;}nsAutoPtr<TimeoutInfo>newInfo(newTimeoutInfo());newInfo->mIsInterval=aIsInterval;newInfo->mId=timerId;if(MOZ_UNLIKELY(timerId==INT32_MAX)){NS_WARNING("Timeout ids overflowed!");mNextTimeoutId=1;}newInfo->mHandler=aHandler;// See if any of the optional arguments were passed.aTimeout=std::max(0,aTimeout);newInfo->mInterval=TimeDuration::FromMilliseconds(aTimeout);newInfo->mTargetTime=TimeStamp::Now()+newInfo->mInterval;nsAutoPtr<TimeoutInfo>*insertedInfo=mTimeouts.InsertElementSorted(newInfo.forget(),GetAutoPtrComparator(mTimeouts));LOG(TimeoutsLog(),("Worker %p has new timeout: delay=%d interval=%s\n",this,aTimeout,aIsInterval?"yes":"no"));// If the timeout we just made is set to fire next then we need to update the// timer, unless we're currently running timeouts.if(insertedInfo==mTimeouts.Elements()&&!mRunningExpiredTimeouts){nsresultrv;if(!mTimer){mTimer=do_CreateInstance(NS_TIMER_CONTRACTID,&rv);if(NS_FAILED(rv)){aRv.Throw(rv);return0;}mTimerRunnable=newTimerRunnable(this);}if(!mTimerRunning){if(!ModifyBusyCountFromWorker(true)){aRv.Throw(NS_ERROR_FAILURE);return0;}mTimerRunning=true;}if(!RescheduleTimeoutTimer(aCx)){aRv.Throw(NS_ERROR_FAILURE);return0;}}returntimerId;}voidWorkerPrivate::ClearTimeout(int32_taId){AssertIsOnWorkerThread();if(!mTimeouts.IsEmpty()){NS_ASSERTION(mTimerRunning,"Huh?!");for(uint32_tindex=0;index<mTimeouts.Length();index++){nsAutoPtr<TimeoutInfo>&info=mTimeouts[index];if(info->mId==aId){info->mCanceled=true;break;}}}}boolWorkerPrivate::RunExpiredTimeouts(JSContext*aCx){AssertIsOnWorkerThread();// We may be called recursively (e.g. close() inside a timeout) or we could// have been canceled while this event was pending, bail out if there is// nothing to do.if(mRunningExpiredTimeouts||!mTimerRunning){returntrue;}NS_ASSERTION(mTimer&&mTimerRunnable,"Must have a timer!");NS_ASSERTION(!mTimeouts.IsEmpty(),"Should have some work to do!");boolretval=true;AutoPtrComparator<TimeoutInfo>comparator=GetAutoPtrComparator(mTimeouts);JS::Rooted<JSObject*>global(aCx,JS::CurrentGlobalOrNull(aCx));// We want to make sure to run *something*, even if the timer fired a little// early. Fudge the value of now to at least include the first timeout.constTimeStampactual_now=TimeStamp::Now();constTimeStampnow=std::max(actual_now,mTimeouts[0]->mTargetTime);if(now!=actual_now){LOG(TimeoutsLog(),("Worker %p fudged timeout by %f ms.\n",this,(now-actual_now).ToMilliseconds()));}AutoTArray<TimeoutInfo*,10>expiredTimeouts;for(uint32_tindex=0;index<mTimeouts.Length();index++){nsAutoPtr<TimeoutInfo>&info=mTimeouts[index];if(info->mTargetTime>now){break;}expiredTimeouts.AppendElement(info);}// Guard against recursion.mRunningExpiredTimeouts=true;// Run expired timeouts.for(uint32_tindex=0;index<expiredTimeouts.Length();index++){TimeoutInfo*&info=expiredTimeouts[index];if(info->mCanceled){continue;}LOG(TimeoutsLog(),("Worker %p executing timeout with original delay %f ms.\n",this,info->mInterval.ToMilliseconds()));// Always check JS_IsExceptionPending if something fails, and if// JS_IsExceptionPending returns false (i.e. uncatchable exception) then// break out of the loop.constchar*reason;if(info->mIsInterval){reason="setInterval handler";}else{reason="setTimeout handler";}RefPtr<Function>callback=info->mHandler->GetCallback();if(!callback){// scope for the AutoEntryScript, so it comes off the stack before we do// Promise::PerformMicroTaskCheckpoint.AutoEntryScriptaes(global,reason,false);// Evaluate the timeout expression.constnsAString&script=info->mHandler->GetHandlerText();constchar*filename=nullptr;uint32_tlineNo=0,dummyColumn=0;info->mHandler->GetLocation(&filename,&lineNo,&dummyColumn);JS::CompileOptionsoptions(aes.cx());options.setFileAndLine(filename,lineNo).setNoScriptRval(true);JS::Rooted<JS::Value>unused(aes.cx());if(!JS::Evaluate(aes.cx(),options,script.BeginReading(),script.Length(),&unused)&&!JS_IsExceptionPending(aCx)){retval=false;break;}}else{ErrorResultrv;JS::Rooted<JS::Value>ignoredVal(aCx);callback->Call(GlobalScope(),info->mHandler->GetArgs(),&ignoredVal,rv,reason);if(rv.IsUncatchableException()){rv.SuppressException();retval=false;break;}rv.SuppressException();}// Since we might be processing more timeouts, go ahead and flush// the promise queue now before we do that.Promise::PerformWorkerMicroTaskCheckpoint();NS_ASSERTION(mRunningExpiredTimeouts,"Someone changed this!");}// No longer possible to be called recursively.mRunningExpiredTimeouts=false;// Now remove canceled and expired timeouts from the main list.// NB: The timeouts present in expiredTimeouts must have the same order// with respect to each other in mTimeouts. That is, mTimeouts is just// expiredTimeouts with extra elements inserted. There may be unexpired// timeouts that have been inserted between the expired timeouts if the// timeout event handler called setTimeout/setInterval.for(uint32_tindex=0,expiredTimeoutIndex=0,expiredTimeoutLength=expiredTimeouts.Length();index<mTimeouts.Length();){nsAutoPtr<TimeoutInfo>&info=mTimeouts[index];if((expiredTimeoutIndex<expiredTimeoutLength&&info==expiredTimeouts[expiredTimeoutIndex]&&++expiredTimeoutIndex)||info->mCanceled){if(info->mIsInterval&&!info->mCanceled){// Reschedule intervals.info->mTargetTime=info->mTargetTime+info->mInterval;// Don't resort the list here, we'll do that at the end.++index;}else{mTimeouts.RemoveElement(info);}}else{// If info did not match the current entry in expiredTimeouts, it// shouldn't be there at all.NS_ASSERTION(!expiredTimeouts.Contains(info),"Our timeouts are out of order!");++index;}}mTimeouts.Sort(comparator);// Either signal the parent that we're no longer using timeouts or reschedule// the timer.if(mTimeouts.IsEmpty()){if(!ModifyBusyCountFromWorker(false)){retval=false;}mTimerRunning=false;}elseif(retval&&!RescheduleTimeoutTimer(aCx)){retval=false;}returnretval;}boolWorkerPrivate::RescheduleTimeoutTimer(JSContext*aCx){AssertIsOnWorkerThread();MOZ_ASSERT(!mRunningExpiredTimeouts);NS_ASSERTION(!mTimeouts.IsEmpty(),"Should have some timeouts!");NS_ASSERTION(mTimer&&mTimerRunnable,"Should have a timer!");// NB: This is important! The timer may have already fired, e.g. if a timeout// callback itself calls setTimeout for a short duration and then takes longer// than that to finish executing. If that has happened, it's very important// that we don't execute the event that is now pending in our event queue, or// our code in RunExpiredTimeouts to "fudge" the timeout value will unleash an// early timeout when we execute the event we're about to queue.mTimer->Cancel();doubledelta=(mTimeouts[0]->mTargetTime-TimeStamp::Now()).ToMilliseconds();uint32_tdelay=delta>0?std::min(delta,double(UINT32_MAX)):0;LOG(TimeoutsLog(),("Worker %p scheduled timer for %d ms, %"PRIuSIZE" pending timeouts\n",this,delay,mTimeouts.Length()));nsresultrv=mTimer->InitWithCallback(mTimerRunnable,delay,nsITimer::TYPE_ONE_SHOT);if(NS_FAILED(rv)){JS_ReportErrorASCII(aCx,"Failed to start timer!");returnfalse;}returntrue;}voidWorkerPrivate::UpdateContextOptionsInternal(JSContext*aCx,constJS::ContextOptions&aContextOptions){AssertIsOnWorkerThread();JS::ContextOptionsRef(aCx)=aContextOptions;for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->UpdateContextOptions(aContextOptions);}}voidWorkerPrivate::UpdateLanguagesInternal(constnsTArray<nsString>&aLanguages){WorkerGlobalScope*globalScope=GlobalScope();if(globalScope){RefPtr<WorkerNavigator>nav=globalScope->GetExistingNavigator();if(nav){nav->SetLanguages(aLanguages);}}for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->UpdateLanguages(aLanguages);}}voidWorkerPrivate::UpdatePreferenceInternal(WorkerPreferenceaPref,boolaValue){AssertIsOnWorkerThread();MOZ_ASSERT(aPref>=0&&aPref<WORKERPREF_COUNT);mPreferences[aPref]=aValue;for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->UpdatePreference(aPref,aValue);}}voidWorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext*aCx,JSGCParamKeyaKey,uint32_taValue){AssertIsOnWorkerThread();// XXX aValue might be 0 here (telling us to unset a previous value for child// workers). Calling JS_SetGCParameter with a value of 0 isn't actually// supported though. We really need some way to revert to a default value// here.if(aValue){JS_SetGCParameter(aCx,aKey,aValue);}for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aKey,aValue);}}#ifdef JS_GC_ZEALvoidWorkerPrivate::UpdateGCZealInternal(JSContext*aCx,uint8_taGCZeal,uint32_taFrequency){AssertIsOnWorkerThread();JS_SetGCZeal(aCx,aGCZeal,aFrequency);for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->UpdateGCZeal(aGCZeal,aFrequency);}}#endifvoidWorkerPrivate::GarbageCollectInternal(JSContext*aCx,boolaShrinking,boolaCollectChildren){AssertIsOnWorkerThread();if(!GlobalScope()){// We haven't compiled anything yet. Just bail out.return;}if(aShrinking||aCollectChildren){JS::PrepareForFullGC(aCx);if(aShrinking){JS::GCForReason(aCx,GC_SHRINK,JS::gcreason::DOM_WORKER);if(!aCollectChildren){LOG(WorkerLog(),("Worker %p collected idle garbage\n",this));}}else{JS::GCForReason(aCx,GC_NORMAL,JS::gcreason::DOM_WORKER);LOG(WorkerLog(),("Worker %p collected garbage\n",this));}}else{JS_MaybeGC(aCx);LOG(WorkerLog(),("Worker %p collected periodic garbage\n",this));}if(aCollectChildren){for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->GarbageCollect(aShrinking);}}}voidWorkerPrivate::CycleCollectInternal(boolaCollectChildren){AssertIsOnWorkerThread();nsCycleCollector_collect(nullptr);if(aCollectChildren){for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->CycleCollect(/* dummy = */false);}}}voidWorkerPrivate::MemoryPressureInternal(){AssertIsOnWorkerThread();RefPtr<Console>console=mScope?mScope->GetConsoleIfExists():nullptr;if(console){console->ClearStorage();}console=mDebuggerScope?mDebuggerScope->GetConsoleIfExists():nullptr;if(console){console->ClearStorage();}for(uint32_tindex=0;index<mChildWorkers.Length();index++){mChildWorkers[index]->MemoryPressure(false);}}voidWorkerPrivate::SetThread(WorkerThread*aThread){if(aThread){#ifdef DEBUG{boolisOnCurrentThread;MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));MOZ_ASSERT(isOnCurrentThread);}#endifMOZ_ASSERT(!mPRThread);mPRThread=PRThreadFromThread(aThread);MOZ_ASSERT(mPRThread);}else{MOZ_ASSERT(mPRThread);}constWorkerThreadFriendKeyfriendKey;RefPtr<WorkerThread>doomedThread;{// Scope so that |doomedThread| is released without holding the lock.MutexAutoLocklock(mMutex);if(aThread){MOZ_ASSERT(!mThread);MOZ_ASSERT(mStatus==Pending);mThread=aThread;mThread->SetWorker(friendKey,this);if(!mPreStartRunnables.IsEmpty()){for(uint32_tindex=0;index<mPreStartRunnables.Length();index++){MOZ_ALWAYS_SUCCEEDS(mThread->DispatchAnyThread(friendKey,mPreStartRunnables[index].forget()));}mPreStartRunnables.Clear();}}else{MOZ_ASSERT(mThread);mThread->SetWorker(friendKey,nullptr);mThread.swap(doomedThread);}}}WorkerCrossThreadDispatcher*WorkerPrivate::GetCrossThreadDispatcher(){MutexAutoLocklock(mMutex);if(!mCrossThreadDispatcher&&mStatus<=Running){mCrossThreadDispatcher=newWorkerCrossThreadDispatcher(this);}returnmCrossThreadDispatcher;}voidWorkerPrivate::BeginCTypesCall(){AssertIsOnWorkerThread();// Don't try to GC while we're blocked in a ctypes call.SetGCTimerMode(NoTimer);}voidWorkerPrivate::EndCTypesCall(){AssertIsOnWorkerThread();// Make sure the periodic timer is running before we start running JS again.SetGCTimerMode(PeriodicTimer);}boolWorkerPrivate::ConnectMessagePort(JSContext*aCx,MessagePortIdentifier&aIdentifier){AssertIsOnWorkerThread();WorkerGlobalScope*globalScope=GlobalScope();JS::Rooted<JSObject*>jsGlobal(aCx,globalScope->GetWrapper());MOZ_ASSERT(jsGlobal);// This MessagePortIdentifier is used to create a new port, still connected// with the other one, but in the worker thread.ErrorResultrv;RefPtr<MessagePort>port=MessagePort::Create(globalScope,aIdentifier,rv);if(NS_WARN_IF(rv.Failed())){rv.SuppressException();returnfalse;}GlobalObjectglobalObject(aCx,jsGlobal);if(globalObject.Failed()){returnfalse;}RootedDictionary<MessageEventInit>init(aCx);init.mBubbles=false;init.mCancelable=false;init.mSource.SetValue().SetAsMessagePort()=port;if(!init.mPorts.AppendElement(port.forget(),fallible)){returnfalse;}RefPtr<MessageEvent>event=MessageEvent::Constructor(globalObject,NS_LITERAL_STRING("connect"),init,rv);event->SetTrusted(true);nsCOMPtr<nsIDOMEvent>domEvent=do_QueryObject(event);nsEventStatusdummy=nsEventStatus_eIgnore;globalScope->DispatchDOMEvent(nullptr,domEvent,nullptr,&dummy);returntrue;}WorkerGlobalScope*WorkerPrivate::GetOrCreateGlobalScope(JSContext*aCx){AssertIsOnWorkerThread();if(!mScope){RefPtr<WorkerGlobalScope>globalScope;if(IsSharedWorker()){globalScope=newSharedWorkerGlobalScope(this,WorkerName());}elseif(IsServiceWorker()){globalScope=newServiceWorkerGlobalScope(this,ServiceWorkerScope());}else{globalScope=newDedicatedWorkerGlobalScope(this,WorkerName());}JS::Rooted<JSObject*>global(aCx);NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx,&global),nullptr);JSAutoCompartmentac(aCx,global);// RegisterBindings() can spin a nested event loop so we have to set mScope// before calling it, and we have to make sure to unset mScope if it fails.mScope=Move(globalScope);if(!RegisterBindings(aCx,global)){mScope=nullptr;returnnullptr;}JS_FireOnNewGlobalObject(aCx,global);}returnmScope;}WorkerDebuggerGlobalScope*WorkerPrivate::CreateDebuggerGlobalScope(JSContext*aCx){AssertIsOnWorkerThread();MOZ_ASSERT(!mDebuggerScope);RefPtr<WorkerDebuggerGlobalScope>globalScope=newWorkerDebuggerGlobalScope(this);JS::Rooted<JSObject*>global(aCx);NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx,&global),nullptr);JSAutoCompartmentac(aCx,global);// RegisterDebuggerBindings() can spin a nested event loop so we have to set// mDebuggerScope before calling it, and we have to make sure to unset// mDebuggerScope if it fails.mDebuggerScope=Move(globalScope);if(!RegisterDebuggerBindings(aCx,global)){mDebuggerScope=nullptr;returnnullptr;}JS_FireOnNewGlobalObject(aCx,global);returnmDebuggerScope;}#ifdef DEBUGvoidWorkerPrivate::AssertIsOnWorkerThread()const{// This is much more complicated than it needs to be but we can't use mThread// because it must be protected by mMutex and sometimes this method is called// when mMutex is already locked. This method should always work.MOZ_ASSERT(mPRThread,"AssertIsOnWorkerThread() called before a thread was assigned!");nsCOMPtr<nsIThread>thread;nsresultrv=nsThreadManager::get().GetThreadFromPRThread(mPRThread,getter_AddRefs(thread));MOZ_ASSERT(NS_SUCCEEDED(rv));MOZ_ASSERT(thread);boolcurrent;rv=thread->IsOnCurrentThread(¤t);MOZ_ASSERT(NS_SUCCEEDED(rv));MOZ_ASSERT(current,"Wrong thread!");}#endif // DEBUGNS_IMPL_ISUPPORTS_INHERITED0(ExternalRunnableWrapper,WorkerRunnable)template<classDerived>NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::EventTarget)template<classDerived>NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::EventTarget)template<classDerived>NS_INTERFACE_MAP_BEGIN(WorkerPrivateParent<Derived>::EventTarget)NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)NS_INTERFACE_MAP_ENTRY(nsIEventTarget)NS_INTERFACE_MAP_ENTRY(nsISupports)#ifdef DEBUG// kDEBUGWorkerEventTargetIID is special in that it does not AddRef its// result.if(aIID.Equals(kDEBUGWorkerEventTargetIID)){*aInstancePtr=this;returnNS_OK;}else#endifNS_INTERFACE_MAP_ENDtemplate<classDerived>NS_IMETHODIMPWorkerPrivateParent<Derived>::EventTarget::DispatchFromScript(nsIRunnable*aRunnable,uint32_taFlags){nsCOMPtr<nsIRunnable>event(aRunnable);returnDispatch(event.forget(),aFlags);}template<classDerived>NS_IMETHODIMPWorkerPrivateParent<Derived>::EventTarget::Dispatch(already_AddRefed<nsIRunnable>aRunnable,uint32_taFlags){// May be called on any thread!nsCOMPtr<nsIRunnable>event(aRunnable);// Workers only support asynchronous dispatch for now.if(NS_WARN_IF(aFlags!=NS_DISPATCH_NORMAL)){returnNS_ERROR_UNEXPECTED;}RefPtr<WorkerRunnable>workerRunnable;MutexAutoLocklock(mMutex);if(!mWorkerPrivate){NS_WARNING("A runnable was posted to a worker that is already shutting ""down!");returnNS_ERROR_UNEXPECTED;}if(event){workerRunnable=mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget());}nsresultrv=mWorkerPrivate->DispatchPrivate(workerRunnable.forget(),mNestedEventTarget);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}returnNS_OK;}template<classDerived>NS_IMETHODIMPWorkerPrivateParent<Derived>::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,uint32_t){returnNS_ERROR_NOT_IMPLEMENTED;}template<classDerived>NS_IMETHODIMPWorkerPrivateParent<Derived>::EventTarget::IsOnCurrentThread(bool*aIsOnCurrentThread){// May be called on any thread!MOZ_ASSERT(aIsOnCurrentThread);MutexAutoLocklock(mMutex);if(!mWorkerPrivate){NS_WARNING("A worker's event target was used after the worker has !");returnNS_ERROR_UNEXPECTED;}*aIsOnCurrentThread=mWorkerPrivate->IsOnCurrentThread();returnNS_OK;}template<classDerived>NS_IMETHODIMP_(bool)WorkerPrivateParent<Derived>::EventTarget::IsOnCurrentThreadInfallible(){// May be called on any thread!MutexAutoLocklock(mMutex);if(!mWorkerPrivate){NS_WARNING("A worker's event target was used after the worker has !");returnfalse;}returnmWorkerPrivate->IsOnCurrentThread();}BEGIN_WORKERS_NAMESPACEWorkerCrossThreadDispatcher*GetWorkerCrossThreadDispatcher(JSContext*aCx,constJS::Value&aWorker){if(!aWorker.isObject()){returnnullptr;}WorkerPrivate*w=nullptr;UNWRAP_OBJECT(Worker,&aWorker.toObject(),w);MOZ_ASSERT(w);returnw->GetCrossThreadDispatcher();}// Force instantiation.templateclassWorkerPrivateParent<WorkerPrivate>;END_WORKERS_NAMESPACE